No posts in a week, I have been silent too long. Blame it on Io!
You see, Io is a contagious disease. It affects programmers’ brains. The incubation period is extremely short, due to the simplicity of its syntax. But the damages are extensive, particularly for Algol-derivative programmers: you will end up messed up! Huh… I forgot to mention the vectors include reading posts about Io. I just hope your infection will not be as bad as mine. (If I do not get google hits from E.R. fans with this paragraph, what will it take?)
First things first, let’s get the token “Hello World!” implementation out of the way:
"Yo Io!" print
Aside from my poor taste in greeting phrases, you probably noticed how concise and simple this statement is. At first sight, to most programmers, it appears to be written backward. Not so if you think that the
"Yo Io!" object — which happens to be a string — is being sent the message
print. Everything in Io is an object, and I really mean everything: lists, files, strings, numbers and even messages!
Attack of the clones
Well that is not fully accurate, to be pedantic, I should have written: everything in Io is a prototype. Indeed, Io is a prototype-based language. C++/Java/C# programmers, here comes surprise #1: Io knows nothing about classes. In class-based languages, classes describes the structure and behaviour of objects in term of methods whereas instances hold the objects’ data. Prototype-based languages do not make this distinction. A prototype is both a class and an instance. To get a new instance, you clone an existing prototype. To get a new class you add or alter the behaviour of an existing prototype. Sounds complex? Let your inner mad scientist free:
Sheep := Object clone
Sheep is defined as a clone of
Object — the parent of all Io prototypes.
Sheep legCount := 4
In brain-damaged English, the
legCount property of a
Sheep is defined as four. Non-programmers would have you believe this is equivalent to: a sheep has four legs.
MutantSheep := Sheep clone
MutantSheep is defined as a clone of a
Sheep. No surprise here, we all knew that!
MutantSheep legCount = 7
legCount property is set to seven! Most inner mad scientists tend to go for six legs, but my inner mad scientist is also nonconformist. I explain the difference between
:= in the next section. Just pretend they are the same for the moment.
dolly := MutantSheep clone
Here be dolly the mutant sheep! The convention is to use an upper-case character at the beginning of the name of prototypes acting as classes and a lower-case character for prototypes acting as instances.
Lots of slots
Once again, I have to confess that I have been inaccurate: I wrote
legCount was a property of a
Sheep whereas, in Io speak, it is actually a slot of
Sheep. A slot associates a value to a name within the context of a prototype.
Sheep holds one slot named
legCount with the value
4. Here comes surprise #2: slots can contain variables and methods.
Sheep baa := method("bah!" print)
baa slot on
Sheep is defined as a method which prints “bah!”. Simple, no?
You noticed earlier that Io has two different assignment operators:
=. Both of them are actually methods on
:= is actually parsed as
= is parsed as
updateSlot. From the method names you probably inferred the difference:
:= creates a new slot on a prototype from a name and a value whereas
= updates an existing slot with a new value.
Io interprets the previous code sample like this:
Sheep setSlot("baa", method("bah!" print))
There is madness to Io’s methods
In case you were wondering,
method is a method on
Object that creates anonymous methods. The self-referential aspect of what I just wrote surely enlightened you, not. Rewind. Slow motion. The
method method takes a list of parameters. All but the last one are arguments to the created method. The last argument is the body of the method. In this example, I define the
growMoreLegs slot on
MutantSheep to be a method with one parameter:
MutantSheep growMoreLegs := method(n,
legCount = legCount + n)
Apart from the slightly surprising syntax, I hope this all sounds pretty familiar so far. Things are about to change big time! Surprise #3: a method can control when and if to evaluate an argument. This lets us define flow control constructs (if, unless, while, etc.) as methods. Most of them already exist on
Object of course. One construct is missing though and we will implement it here. But first, we will botch the job:
Object unless := method(cond, then, else,
if(cond, else, then))
else are evaluated when unless is called. We do not want that. What we want is: unless
cond is true, evaluate
then, otherwise evaluate
else. In Io this is done like this:
Object unless := method(
if(call eval argAt(0),
call eval argAt(2),
call eval argAt(1)))
Update: see jer’s comment for a better implementation
What looks like a nice trick, is actually extremely powerful. Lisp programmers laugh in the background, they’ve used it successfully since 1958! I will not explore this vast topic any further in this post — I must save some ammunition for future posts!
By the way, I hope you appreciated the fact that Io is a dynamic language: we just added a method to the
Lobby for Io!
Before concluding, I would like to point out that
MutantSheep are defined like slots. But what is the underlying prototype? In Io, this is the
Lobby. So what? Well,
Lobby ultimately derives from the
Object prototype, like all other prototypes. But where do you think we found the
Object prototype? Bingo! It is a slot in the
Protos slot of
Lobby. I suggest you read this again. You are not dreaming:
Object is both a slot of a slot in the
Lobby and an ancestor of the same
Lobby. How is that for another self-referential bit of self-reference?
Time to conclude, I hope this introduction will entice you to dwell deeper into this really amazing language. Believe me, we have only scratched the surface!
For a more complete, formal and accurate presentation of Io, check out The Io Programming Language.
Visit the official website or join the posse on irc.freenode.net#io.