Io Has A Very Clean Mirror

January 5th 03:54
by why

The best languages are deceptively simple. Io has an incredibly brief grammar, so it’s easy to think you’ve learned everything with one short perusal. And the manual is quite short, too, isn’t it? Nothing to it, right?

Io is just an incredibly hushed secret. (Perhaps because it is impossible to Google stuff about it.) Did you know that Io’s introspection and meta tricks put Ruby to serious shame? Where Ruby once schooled Java, Io has now pogoed.

Here, let’s start with a simple block:

Io> plus := block(a, b, a + b)
==> method(a, b, 
        a + b
Io> plus call(2, 3)
==> 5

Okay, so the block works. The plus block added two numbers.

Now let’s do some introspection on this little fellow.

Io> plus argumentNames
==> list("a", "b")
Io> plus code
==> block(a, b, a +(b))
Io> plus message name
==> a
Io> plus message next
==> +(b)
Io> plus message next name
==> +

Hot holy cold mold. Not only can you get the names of the block params. And not only can you get a string of the block’s complete source code. You can sneak into the code and traverse the messages inside. And most amazing of all: it’s awfully easy and natural. True to Io’s quest. Ruby’s mirror can’t see any of that.

But, whoa whoa, hey now, don’t touch that dial.

Io> plus message next setName("-")
==> -(b)
Io> plus
==> method(a, b, 
        a - b
Io> plus call(2, 3)
==> -1

Gunpowder and treason! Now THAT is a monkeypatch to the viscera. Subtraction is in effect.

See, I first had Io pegged as a Python. Simple rules, very strict, few sigils, very little sugar, both adhering strongly to their own manifestos. No way, though. Python keeps trying to shed its functional skin, only to reveal its acid wash jeans underneath. Ironed and starched and pegged.

Io pays obeisance to minimalism, but somehow cracks the door open on a wild and uncouth gymnasium of meta magic. And it goes so much deeper than these examples. I don’t know if Io is trying to be rebellious, but its malleability goes far beyond Ruby. And, I’d say it goes beyond Smalltalk and Self and Lisp, since it makes this kind of ripping and tearing completely effortless.

Now begin the comments …



said on January 5th 06:34

The way concurrency works in io is fun for making little prototypes (pun intended!)


said on January 5th 08:37

IO is pretty amazing. You’d probably also like Factor.


said on January 5th 09:12

Wow, you’re right (and as a plus, it uses Git as its repository, great). And you’re right. It is not easy to find good info about Io from Google. I would love to see a minimalist Poignant Guide to Io from you! :-)

Brian Mitchell

said on January 5th 09:14

Indeed. Io has been a stage to some of the craziest meta-programming I’ve seen anywhere. It has come a long way since its earlier debuts years ago… so I’d encourage people to check out the source and join conversation with the community. There are some exciting developments so now would be a good time to dive in.

Mod Serf

said on January 5th 09:46

I, for one, bow to my new Io overlords.


said on January 5th 09:48

I’m scared. The universe is loosing is bounds and start to fibble away in a meta fashion style. What you just showed are simple operations with an enormously complicated potential.

  • don’t forget luke : at the end, everything still goes trough the CPU


said on January 5th 09:51

You might like to check out the ReFLect language. Equivalent code is

let plus = {| \a.\b.a+b |};
let change2minus {| \`s.\`t.`p `q `r |} = {| \`s. \`t. `q–`r |};
eval {| `(change2minus plus) 2 3 |};
: {|–1 |}

\x.E is a lambda expression. Quotes such as `s stand in for pattern matching within a term. So {| \`s.\`t.\`p `q `r |} matches any function with two arguments that consists of a function application with two arguments–s and t are the outer arguments, and p is the inner function being applied (in this case, +).

In essense, reflect makes tearing apart code even easier, because you can pattern match code expressions.

Max Battcher

said on January 5th 10:23

I think a Lisp hacker would give you a “young whippersnapper” lesson and explain properly how all of that is possible in Lisp, particularly if you were one of the few to use a Lisp Machine in that amazing era of dinosaurs. Io simply takes that sauce and places it entirely within a minimalist OO goodness ala Self and that’s probably the biggest reason it seems different from the Lisp way of doing this sort of thing in that the message tree looks like a nice collection of objects to those of us long schooled in the ways of OO. Io’s a good language and something that I like returning to once in a while as a damn pretty sandbox. I don’t think it is rebelliousness, only a deep respect for the history of languages (Steve seems to have a great knowledge of this) and a wish to do something like classic Lisp or Self in the form factor of Lua more than anything.

PS, don’t be hating on my jeans… they are quite pragmatic for working in.

tesing 123

said on January 5th 10:53

this is a test!


said on January 5th 11:02

Damn it, I just had my first apocalyptic vision in 3 years. People really really can’t be trusted with this kind of power, okay? As you gaze into the mirror, the mirror also gazes into you. Turn back while you still can!


said on January 5th 11:04

(I almost forgot, insert bad joke here about how replacing the math operators is already affecting the comment system. Don’t say I didn’t warn you, is all I’m saying.)


said on January 5th 11:07

testing is stupid!

Test with Yahoo!

said on January 5th 11:15


said on January 5th 11:27

However, let’s not forget a few weak points currently on Io (and I will only mention things which can be improved)

–Makefile based compilation only (gnu autoconfigure at least provides a “fine tuning”).
–Windows binaries lag behind. It is more than a half year now, where one ultimately wonders how much attention Io gets on non-mac plattforms.
–Documentation. The documentation is partially ok–but partially it is simply not existing, where one is to be expected to look at the source.

I am sure the two first points can easily be solved, but the third one is one that had Ruby troubled for a long time until the Pickaxe book emerged.

But where is a Pickaxe-for-Io?


said on January 5th 11:30

Akita: Perhaps we could give Io an informal moniker (such as: Schneider) which could be more easily distinguished by the robots.

ReFLect: Oh, good show. Any other codes from the crowd are welcome.


said on January 5th 11:37

To “peruse” means to “read through with thoroughness or care.”


said on January 5th 11:47

Arthur: I have been working for quite a while on an “Io Tome”. It exists mostly in my head at the moment, due to other projects coming up. One of these days I’ll have enough time to sit down and work out a draft which other people can refine. Documentation does sorely lack behind in Io right now.

As far as Windows support goes, that is remarkably good considering none of the core contributors use windows as a platform, and we only ever see fading interest in Io running ontop of windows. Perhaps if there was more interest from the user base, it would improve, and that would be nice to see!

The build system is a source of huge contention. We all tend to agree that autotools isn’t exactly the best thing out there, where we deviate (at least myself and Steve Dekorte) is that it’s a better system than what we have right now, less fragile. He’s not against using it, someone just needs to come forth with a patchset demonstrating its usefulness, and he’ll consider it (at least, this was his position a year ago when I last discussed it with him—then I started a new job and have had no time since).

In any event, Io is an extremely young language, with some quirks and gotchas. Overall, it has great potential however.


said on January 5th 12:57

Holy crap your comments section just imploded.


said on January 5th 13:16

It’d be nice if you added more step-by-step explanations of the code snippets, for those of us who never touched Io. It looks very interesting.


said on January 5th 13:46

Tip for Googling: search for “Io language”. Slightly fewer results about I/O and a lot fewer about the Jovian moon and Greek mythology.


said on January 5th 13:51

Pupeno: I’ll explain the code steps one by one.

plus := block(a, b, a + b)

This line of code assigns a lexically scoped method (or block) that takes two formal parameters, a and b, and adds those two parameters returning the result, to the method “plus”. That is to say, it assigns that block to plus, calling plus will return the results if given two arguments.

plus call(2, 3)

Unlike in some other languages, blocks do not automatically activate like methods or functions do. In Io, you must explicitly “call” a block. This line of code activates the block in the “plus” slot (think "variable") with two arguments, 2 and 3, returning the expected result “5”.

plus argumentNames

Here we ask the block for the names of the formal parameters. It returns a list of items in string representation.

plus code

This line of code asks the interpreter for the code representing the entire method. Appending a “;” message to the string returned here can cause your method to be serializable.

plus message name

This line of code asks the interpreter for the message object of the body of the method (in Io by convention, the body of a control structure is always the last argument passed in, usually implicitly, since methods and blocks in Io can accept any number of arguments, it’ll just ignore the ones not used in the body) then it asks for the string representation of the first item in that message tree, in this case, the string “a”.

plus message next

Like the above, but instead of asking for the name of the message, we ask for the object representing the next message in the body of the block (the “+” method with the argument "b").

plus message next name

Like above, but we ask for the message object, then skip to the next message in that tree, and ask for its name, in this case “+”.

The other examples are pretty easy to decipher, “set” methods just set the value of the objects I listed above.

So if you think of messages in Io as a simple tree (which is essentially, what they are when you include arguments), then “next” is like skipping down the left side of the tree, and arguments are like skipping down the right side of the tree. Each node in this tree has several pieces of state and behaviour that you can ask about.


said on January 5th 13:51

Grr, sorry about the crappy formatting folks. I’d edit it if I could.


said on January 5th 14:19

There you are. Very generous, jer. Interesting about the semicolon message. I’ve been running into that alot but hadn’t stopped to look at it.


said on January 5th 16:32

As soon as I get my MacBook I plan to jump wholeheartedly back into the Io game. I love me some Io.


said on January 5th 17:03

Thanks for fixing up the formatting _why, should use Preview next time first. :)

I should make one more note about serialization. Not everything is serializable at the present time in Io. For instance, CFunction objects (that is, for instance, “clone” on the Object method (and as a result, all objects respond to it unless you get into some bit of magic, which I won’t describe here)) do not know about their source code. They could, but it’d involve modifying the source before it’s compiled, which is not only silly, it’s superfluous. Many methods in Io respond to the “serialized” method which returns a string representation of the receiver of that message, which is extremely handy in many cases. For instance:

Io> list(1, 2, 3) serialized
==> list(1, 2, 3);


said on January 5th 19:37

Ya, Io is fun stuff. Now it just needs to get its feet wet in the coder pool (& vice versa). Ruby is only the cool kid now because he has a smooth library and a killer app. Coding Io might become the next “elegant programming” fashion statement.


said on January 5th 20:13

Smalltalk = Simula.messaging + AlanKay.inspiration
Ruby = the_good_bits_of(Perl + Python) + some_of(Smalltalk)
Io = the_rest_of(Smalltalk) + Ruby


said on January 5th 21:40

Working with Io on Windows every day. I have a bunch of Windows patches that I need to submit when I get a chance. If anyone has any windows questions, feel free to ask.

crabby from a cold

said on January 6th 01:49

This is supposed to be elegant? I hate to say it, but even “public static void” looks more parseable.


said on January 6th 13:37

crabby: Why’s example is pretty contrived, but the idea is that the better you can reflect onto the language itself (although not completely, as per Gödel), the DRYer your code can get. Elegance doesn’t have to do with appearances, necessarily. From Wikipedia, “elegance is the attribute of being impressively effective, yet slighty simplistic.” Lisp is considered by some to be elegant, although on a functional level, as opposed to OO, but few would call its syntax pretty.

Of course, you could always make your own keywords:

def := Object clone
def forward := method(
  method_name := call message name
  call message setName("method")
  call sender setSlot(method_name, resend)
class := Object clone
class forward := method(
  new_class := Object clone
  call sender setSlot(call message name, new_class)
  call message argsEvaluatedIn(new_class) first

# You can't reopen classes or methods with these,
# (although there's probably some trick to allow it)
# but now you can define them in a Ruby-ish way:

class Norton(
  def scan(thing,
    "Scanning #{thing}...\n" interpolate print
    "Done! No viruses found." print


said on January 6th 17:53

Not exactly “re-opening” as they’re never closed, but anyway, you can do something like this (keeping in mind, newSlot is similar to attr_accessor in Ruby):

Norton do(
  newSlot("blockEverything", false)

  def lockOutUser(setBlockEverything(true))


said on January 7th 01:12

Anyone managed to build Io on OSX using macports (Io-2007-03-21.tar.gz)?


said on January 7th 07:22

2007-03-21 is historic. I would highly recommend you grab the new tarball pushed out last night from or download the git repository and build from that.


said on January 7th 11:30

If you’re not averse to rolling up your sleeves, you can take the existing Io Portfile and add the latest tarball location+md5 hashes to it to get the latest nightly build. I would, however, check your compile environment first as I’ve never gotten build errors like that on up-to-date machines.


said on January 7th 11:31

Thanks jer…that did the trick. Now to explore…


said on January 7th 16:41

so… are you leaving ruby ? just curious what this really is on top of things


said on January 7th 22:31

tripdragon: What? Ruby’s not dead, and Io isn’t super cool yet. It doesn’t even have hashes! We can still play with it, though.

class := Object clone
class forward := method(
  prototypes := call evalArgs
  new_class := if(call hasArgs, prototypes removeAt(0), Object) clone
  prototypes foreach(prototype, new_class appendProto(prototype))
  new_class className := call message name
  call sender setSlot(new_class className, new_class)

# Everyone loves multiple inheritance!

class Thing do(isPants := false)
class Pants(Thing) do(isPants := true)
class Square(Thing)
class Trousers(Pants)
class SquareTrousers(Square, Trousers)

if (SquareTrousers proto == Square,
  SquareTrousers protos foreach(thing,
    if(thing isPants, "Pants", thing className) print))


said on January 8th 01:30

bug: Internet communities are like moving flotillas. And, once you’ve chained up, they like to know when you come and go. Many dads don’t like the screen door left ajar.

Many Dads

said on January 8th 02:46

Speaking of doors and what they like. Will you be at SXSW, perhaps riding a mandolin pony, question.

The power of Assembler

said on January 8th 05:24

In the 60’s and 70’s, power coders would use assembler to get the most out of those old microcontrollers masquerading as mainframes. A primary technique was self-modifying code to avoid re-executing conditionals.

By and by, it turned out that, in large systems, self-modifying code was effectively impossible to debug. It’s a method for hiding bugs. Slowly, it was banned.

Yeah, things aren’t exactly the same, but there are similarities. If my money were resting on the outcome, I’d limit self-modification in some ways, perhaps to boot-time self modding. That way it is at least possible to view a listing of the code that is executing.

If you think about testing, code coverage, provability, auditing, code reviews, and debugging, you’ll see what I mean.

There’s clearly an area for this. Self-modding might be a good base for grad students to quickly generate new languages from Io, in order to experiment with a new or different programming paradigm. And new DSL’s.

But people tend to guess that by trimming the code at run-time you can improve performance. Yeah, to an extent, but self-modding interpreters have never beat straight C for performance. Any interpreted language with ‘eval’ can pull a more-or-less equivalent trick. And we’ve learned to limit that.

Even simple mods like updating DLL’s has been shown to be problematic—the user is running a codebase that is different from the code that was tested. Search for ‘dll hell’ for further info.


said on January 8th 16:28


said on January 10th 02:23

Reflection is not just about self-modding, thank goodness. For more on what’s so great about reflection, look up how it’s used by Smalltalk (and probably also LISP, Ruby etc.).


said on January 11th 02:59

A language need not limit self modifying code. Convention should limit it to very few areas. This feature demonstrated in this post is used sparingly (mostly in control structures) in Io, but yes, it is used.

A language should not impose itself on the programmer. The whole idea is that the programmer imposes himself on the language. Keeping your language as flexible as you need it to be allows you to not only make mistakes and learn from them in a practical manner, by for instance, hard to find bugs; but also learn new techniques that other languages may or may not expose to you.

I find it a win-win opportunity, though I know some folks who have played with Io have run away screaming at this very feature (mostly those who did, ran straight to Haskell =]).

Comments are closed for this entry.