9.20.2006
5:59 AM

I haven't posted anything in a while! Oh wow. Too long.

So here's something to make up for it. It's a lazy macro: is a function that, once run, modifies the code that calls it. You get a slowdown the first time the code is run, but afterwards, it is as if you wrote the generated code straight into the function. It's an amazingly powerful technique that, coupled with message caching, can do things that you just can't do in most other languages.

Here's a macro that gives us a shortcut: 'foo will become getSlot("foo"). It's really quite handy, as getSlot is a common operation. The ' symbol was chosen because of its use in Lisp: that is, to prevent Lisp from evaluating the symbol. That's similar to what Io does here: by using getSlot, you are retrieving objects that would otherwise activate without activating them.

Object ' := method(
    name := call argAt(0) name
    attached := call argAt(0) attached
    call message setName("getSlot") setArguments(list(name box))
    if(call message attached,
        call message setAttached(call message attached setAttached(attached))
    ,
        call message setAttached(attached)
    )
    call sender getSlot(name)
)

' gives us a small problem, initially. It's actually an operator, and parsing it captures quite a bit more than expected. We only want the first symbol after the quote, so we have to modify the scheme a bit. We take the first argument to ' and move all of its attached methods to the actual message's attached messages.

With this adjustment, we get 'foo bar to look like '(foo) bar instead of '(foo bar).

Of course, that's not the whole story. We rename the message to "getSlot", so that we're calling the function that we want. That's easy enough: just do a call message setName("getSlot"). However, what we have now as an argument to getSlot is just a message. We need to give it a string. And we'll need to store it in a message. How do we do that?

Here I'm going to define a utility function called box. This creates a new message and stores the receiving object inside it, as the cached value, so that when the message is evaluated, the result is our original object.

Object box := method(
    Message clone        setName(getSlot("self") asSimpleString)        setCachedResult(getSlot("self"))
)

Now we just have to box our string up and set it to the arguments of our original message. With the macro complete, it only takes one pass over a ' to change it into a getSlot call.

foo := method(bar,
    'bar println
)

getSlot("foo") println

foo("Hello, Io!")

getSlot("foo") println

foo("Goodbye, Io!")

/*
method(bar, '(bar println))
Hello, Io!
method(bar, getSlot("bar") println)
Goodbye, Io!
*/