Today, we will continue the little stroll around the Io language landscape that we started in Blame it on Io! A slow-paced introduction to the Io language. We will delve deeper into the central principle of prototype-based languages: cloning.

Cloning crash course

For the mad scientists and other evil dictators who landed here after googling “manual of cloning”, here is a crash course in object creation, à-la Io:

Movie := Object clone original := Movie clone original title := "Island of Lost Souls" original year := 1933 original scenario := "Mad scientist off-shores development of mutant race" remake := original clone remake title = "The Island of Dr Moreau" remake year = 1977 remakeOfRemake := remake clone remakeOfRemake year = 1996

Object creation in Io, like Polka, is a two step dance: 1. you clone, 2. you specialize.

Differential inheritance

As we have just seen, cloning an object is done by sending it the clone message. This allows for a very elegant implementation of the Singleton pattern:

GeorgeClooney := FamousActor clone GeorgeClooney clone = GeorgeClooney

Bad news for my wife… but I digress.

To really understand cloning, it is necessary to look at how an object is implemented. I will spare you the details of the struct definition (see vm/IoObject_struct.h if you really want to know) by remaining at a conceptual level. Io objects are made of two ingredients: slots and prototypes. Slots are stored in a hash table and represent both the data and the methods of the object. Prototypes are stored in a list and are the ancestors of the object — more on why this is a list in the next section.

How does this all fit together? Well, when a message is sent to an object, its name is used to query the slots hash table of the object. If no slot is found, the operation is repeated for each prototype in the prototypes list. When a matching slot is found, it is activated. If no slot is found, the forward slot is activated. If no forward slot is found, then you get an error. This will clarify:

JeanClaudeVanDamme := FamousActor clone JeanClaudeVanDamme act

will raise: Exception: Importer Object does not respond to 'act' , whereas:

KeanuReeves := FamousActor clone KeanuReeves forward := method( writeln("I know ", call message name)) KeanuReeves kungfu

will even pretend that Keanu Reeves can act.

Now, back to our first question, what happens when an object is cloned? Very little actually: a new object is created with an empty slots hash table and a prototypes list containing the target of the clone message. By default, a clone delegates everything to its original. Once it is created, a clone is usually specialized by adding slots in its slots hash table. The clone only stores the difference with its original. This is called differential inheritance.

Clones sharing memories with their original, not only makes bad hollywood movies (The Island and Replicant), it is not always desirable:

Movie := Object clone Movie cast := List clone

would lead to all movies sharing the same cast! You could fix the problem by initializing the cast slot to a freshly cloned list on every Movie clone, but this would be error-prone and very inelegant. This is where the init slot comes into play. It is activated, if it is present, just after the creation of the clone. Let’s rewrite the previous piece of code:

Movie := Object clone Movie init := method(self cast := List clone)

Note that self is required, otherwise the cast slot will be local to the method.

Future directions

This is the current state of affair in Io. But things might change, Steve Dekorte, the mad scientist behind Io, identifies 5 different cloning strategies with regards to slots:

nil: the slot is set to nil in the clone — init := method(self foo = nil) shared: the slot is not created in the clone — the current default behaviour shallow: the slot is created with the original’s value — init := method(self foo = foo) deep: the slot is created with a clone of the original’s value — init := method(self foo = foo clone) very deep: the slot is created with a deep clone of the original’s value — init := method(self foo = foo deepClone)

It is probable that support for each strategy will be added to Io at some point. For now, you have to use the init slot to implement these strategies.

Dynamic inheritance with Protos

Let’s come back to the prototypes list. From what I have explained so far, it seems objects can have only one parent, the original. A list looks superfluous. Great news for my mad-scientists readers: the answer does not follow the laws of biology. Io makes it possible to add and remove parents to an object, at runtime, using appendProto , prependProto , removeProto and other related methods. These methods simply manipulate the list of prototypes associated with an Io object.

LukeSkywalker := Person clone LukeSkywalker appendProto(Orphan) LukeSkywalker fightsWith(DarthVader) DarthVader tellsSecretTo(LukeSkywalker) LukeSkywalker removeProto(Orphan) LukeSkywalker appendProto(DarthVader)

And this concludes today’s stroll! I hope you found it informative and entertaining. I would like to thank everyone on the #io channel for your patience in dealing with my questions. Most, if not all, of the information contained in this post comes from chats on IRC and from the Io documentation. I’ll only claim the silly examples!

Share this: Twitter

Facebook

Like this: Like Loading... Related