[ruby-core:32665] Re: Proposal for Optional Static Typing for Ruby

From: Eleanor McHugh <eleanor@...>
Date: 2010-10-01 12:02:13 UTC
List: ruby-core #32665
On 1 Oct 2010, at 02:33, Martin Pilkington wrote:
> Hi Ellie
> 
> What do you mean by runtime mutable? Do you mean creating new classes or methods at runtime? Do you mean creating a new class at runtime? Do you mean adding a method to an existing class at runtime? Swapping the implementation of two methods? Loading new code at any point during runtime? All of those are things that Objective-C can do. The only thing you can't do is to interpret a string containing Obj-C as code, as while Obj-C is heavily runtime based, it is still a pre-compiled language (that said it is theoretically possibly in the future for such JIT compilation to occur).
> 
> Yet despite being very dynamic, it also gives the ability for type information to be added prior to compilation, so tools can use it to help aid developers.

Martin,

I've clearly outlined what runtime mutability is in the context of Ruby. It's the ability to arbitrarily and with bound to make changes to the type space of a program without any reference to the shape of that type space at the time the change occurs. The techniques you mention are a subset of that, and as I stated in my previous response any language _can_ be used this way through abuse of function and void pointers, or similar abstractions. However that's analogous to Greenspun's Law: every significant program includes a buggy, half-arsed implementation of Lisp ;)

In Ruby such ad hoc abuse is possible, but it's not necessary because the language exposes the full power of its compilation facilities via eval and its relatives. Ruby is essentially Lisp for a world of Objects as opposed to Abstract Data Types.

You really need to allow time for Ruby's actual type system to settle fully in your head to appreciate the full implications of this, otherwise you're going to keep labouring under the misguided impression that nominal typing is a significant factor in designing and debugging Ruby code. I know that experience in compiled and statically typed languages suggests this should be the case, but it just isn't. Their type systems are fundamentally different.

In Ruby the use of nominal type-checking leads to brittle code that can and will break the basic expectations of experienced Ruby developers. We have Object::kind_of? which can be used to enforce such constraints but I don't know a single experienced Rubyist who doesn't consider it a code smell for precisely this reason.


Ruby eval, module_eval and instance_eval can each be used to reshape type space - ranging from the global shape to individual details - with very little effort.

I'm sorry to keep coming back to this notion of type space, but in this context it is very important as Ruby objects are better understood as coalesced groups of type information than by reference to Class or similar nominal typing concepts. A Ruby object is generally (but not necessarily) a member of a set of isomorphic types with the class information that you'd recognise in Objective-C or Java actually acting more as a partial derivative on the multi-dimensional set of isomorphic types which it expresses on any given inspection.

Let me put this another way. If a Ruby object were a person then the apparent nominal class upon inspection might be the hat that they've decided to wear, but that wouldn't prevent them from swapping hats to suit the occasion or from wearing completely unsuitable shoes at the same time. Under normal circumstances a Ruby program might well ask whether the object is wearing a hat, and then as we know that hats have a certain shape and set of interactions with their environment we can decide to check its colour or flip the brim if it has one.

Now it's not important whether or not all Ruby objects behave like this all the time. It's only important that for significant objects in a Ruby program they're likely to behave like this enough of the time - popping on hats, taking off coats, swapping cups of tea with their associates - that the correctness of the overall program is determined by the structural types through which they interact and not by a platonic ideal which thanks to isomorphism might be cognate with an infinite number of other platonic ideals whose interactions are indistinguishable in a particular context.

The set of nominal types embodied by a given structural type is infinite. Accepting this and making it productive for the individual programmer is a basic axiom of Ruby's design. The language takes a small performance hit for this, and it scares the hell out of those used to thinking largely in terms of nominal typing, but the payoff is in allowing more flexible and general design: in essence we trade a modest amount of additional processing power and of developer effort in understanding Ruby code for a huge reduction in the amount of code required - often an orders of magnitude reduction. That means having to remember far fewer details of type space and far fewer lines of code which leads to a net gain in effectiveness.

Please don't think I'm arguing that better dev tools which allow us to reason about Ruby code in new and powerful ways aren't desirable. They obviously are. But to add more insight than they do unnecessary verbosity they need to focus on understanding Ruby as is and not on constraining Ruby developers to think in the same way as they might in C or Java. That would be the worst of both worlds: slower runtime performance than statically compiled code but without any of the productivity boost or elegance of exposition that we currently associate with Ruby.

And as I've previously mentioned, the place to look for inspiration in designing such tools is in other languages with unbounded eval and higher-order typing, such as Lisp or Forth.

I'll shut up now.


Ellie

Eleanor McHugh
Games With Brains
http://feyeleanor.tel
----
raise ArgumentError unless @reality.responds_to? :reason



In This Thread