Lazy Bricks, Lazy Mortar

January 10th 15:47
by why

Here’s a bit more Io. Don’t feel obligated to spread this stuff around. I like keeping things here obscure. After RedHanded, it’s nice to have a bit smaller crowd. And this stuff is just written for the few dedicated readers who don’t mind travelling Wherever.

Previously on Hackety Org, the topic was how you can get Io to reflect on its own code. In the parse tree, everything is a message. You can walk through a block and take it apart. The mortar is still soft, so you can move the bricks around.

Here’s a stack of bricks right here:

Builder html(
  head(title("Lazy Bricks, Lazy Mortar"))
  body(
    div(
      p("Here's a bit more Io...")
      p("Previously on Hackety Org...")
    )
    div(
      p("Adieu, friends and uncles.")
    )
  )
) print

So, the parse tree is all messages. Io looks at this and sees an html message with one large argument: all that code inside the parens. That argument is a series of messages as well: head and body.

Most languages would just run all the messages and then get back to you afterward. But just like Neo, pausing time and plucking bullets from the atmosphere, Io gets nice and slow and lazy. We can step through the tree and run each message by hand.

Builder := Object clone
Builder forward := method(
  tag(call message name, call message argAt(0))
)

This forward method intercepts all messages. So the html message is going to hit that slot.

One vital bit here: the method lists no arguments. Its argument list is empty. However, it really does take arguments. Normally, in Io, you’ll use html := method(arg1, arg2, ...) to tell the method to receive two arguments. This would unpause The Matrix, though, and run all the messages like languages normally do.

If you send arguments to a method (or a block) without naming them, then Io will go lazy on us. It’ll attach those arguments to our Call object, but it’ll skip actually running them.

So, the code call message name and call message argAt(0) gets the method name and the arguments from the Call object.

Io> call message name
==> html
Io> call message argAt(0)
==> head(title("Lazy Bricks, Lazy Mortar")) ;
body(div(p("Here's a bit more Io...") ;
p("Previously on Hackety Org...")) ;
div(p("Adieu, friends and uncles.")))

See, all those messages are sitting around, waiting to be ignited! It’s our job to slap these bricks together, kid.

And I’ll bet it’ll take all of this arcane metaprogramming and snake charming, right??

Builder tag := method(name, nodes,
  inner := ""
  while(nodes,
    if(nodes name != ";",
      inner = inner .. if(nodes argCount > 0,
        tag(nodes name, nodes argAt(0)),
        doMessage(nodes))
    )
    nodes = nodes next
  )
  "<#{name}>#{inner}" interpolate
)

A little tricky, but not too bad. We loop through each of the message nodes and build a string as we go. The tricky part is inside the while loop. The if statement skips semicolon messages. All other messages have one of two fates: either recurse into tag if there are more arguments or run the message.

You see, strings are messages, too. (Since they haven’t been turned into strings yet.) They are considered messages which ignore their arguments.

Io> "facts"("opinion")
==> facts

So the doMessage is used to just get us the string, to reify it so we can add it to the bigger HTML string.

This all has given me much to think about. I haven’t been reaching for eval in Io. (Where it’s called doString.) Not as much spelunking for hidden caves to use for storage. All you got is slots and locals. Many limits, many restrictions. But, gladly, a whole lot of built-in messages.

13 comments

alex

said on January 10th 16:48

Do you think you could come up with some imaginative examples on how to do interesting things concurrently with Io? i.e., show how easy it is to go beyond your ruby “5ยข Concurrency” example with the async operator.

Matt Brubeck

said on January 10th 16:51

Io looks like a perfect example of Smart languages and dumb parsers.

Brian Mitchell

said on January 10th 17:01

All literal values in Io code are represented using messages. When messages are run one of two things happen:

1) The message checks a slot on itself (yes, messages are object) called cachedResult. If there is something set it will use it. Otherwise…

2) It will be dispatched to the target object. This is either explicitly given on the left side as an expression (possibly another message) or as an implicit object via the Locals object each scope runs its code in.

I’m looking forward to the entry that introduces removeAllProtos and forward or maybe setIsActivatable. ;-)

Moritz Heidkamp

said on January 10th 19:49

Behold my Brackets module! Can you tell what it does?

Brackets := Object clone

Brackets curlyBrackets := method(
  map := Map clone
  call message arguments foreach(pair, 
    map atPut(pair name asMutable rstrip(";:"), call sender doMessage(pair next))
  )
  return map
)

Brackets squareBrackets := method(
  call message argsEvaluatedIn(call sender)
)

// This is actually a work-around for something I think is a bug and is filed over here: http://iolanguage.com/issues/view.cgi?number=95
Brackets : := method(call target)
Object appendProto(Brackets clone)

Right, Rubyesque array literals and ECMAScriptastic Hash literals!

bug

said on January 10th 22:02

Moritz: Yes, this is the kind of stuff that Io need more of, standard. Of course, you wouldn’t be able to redefine the brackets in another proto without messing things up…methinks Io needs to learn from Ruby about readability (and a pinch more sugar).

bug

said on January 10th 22:04

Also, I apologize for forgetting that Io does indeed have hashes.

Djur

said on January 10th 22:44

bug: But that’s not really an issue, because Io is primarily an extension/embedding language. The fact that it defines as little syntax as possible while leaving the door open for defining the meaning of that syntax is a plus.

Spongebob Squarepants

said on January 10th 23:55

_Why…would love to have a poignant guide to Io once you’re done figuring it out :-)

jer

said on January 11st 02:53

bug: I’d suggest that Io actually have less sugar than it does now, but retaining the hooks into the language so you can define the sugar as you need it. It’s not about dictating a syntax to anyone, it’s about keeping it as simple as possible, yet as flexible as it needs to be for other people to make it complex if they so desire.

_why

said on January 11st 03:21

jer: It is really fascinating how Io has so many hooks that it doesn’t exploit itself. It’s like a patted-down schoolmarm whose subliminally teaching anarchy. Or like a very tiny congress that’s only there to make sure you have gobs and gobs of freedom. Do with it what you like, the language is yours to ruin, complements of the house.

bug

said on January 11st 11:31

OK, Djur, that’s fair, and I guess now I like *jer*’s idea, too. It just almost seemed that Io was trying to be a better Ruby. It could be, if it wanted to, but then Ruby would be thrown out to the streets, rolling off clever one-liners for food. But Io doesn’t want that, I suppose. It wants you to make your own language from the bottom up. It’s a big ball of language clay, snatched from the skies of Jupiter. Of course, that means that once you mould your Io into a swan that winks slyly at everyone, your friend’s Io, which is now a monkey with glasses and a typewriter, can’t talk to yours. Ruby is the shiny red toolbox with mostly everything that mostly everyone needs, and also looks pretty. (and I wonder why Mac-ies like Ruby so much!)

steve

said on January 11st 19:58

The compatibility of customizations issue can be somewhat solved by how io doesn’t have globals–so just put your customizations in your package’s namespace.

bleh

said on January 12nd 15:48

Why, you will notice that in time this blog will have a pretty great audience too. You did great with the Redhanded and people get attracted by your geniusity.

Comments are closed for this entry.