torsdag, februari 14, 2008

Silly Io experiment

I have known for some time that Io is quite powerful as a language. I wanted to check if it was also suited for creating DSLs. Since I'm lazy I didn't want to create a new DSL just for this, so I decided to appropriate the associations language from ActiveRecord.

The process I followed was this - first come up with something that looks passably nice, then try to implement it. The only requirements was that the names of the associations were saved away somewhere, once per prototype for each model. This is the syntax I came up with (well OK, I didn't work very long on it...):
Post := IoRecord {
has many authors
belongs to blog
belongs to isp
}

Author := IoRecord {
has many blogs
has many posts
has one name
}
Actually, this looks almost readable, right? It's all valid Io code. The question is, how do we get it to do what we want? Without further ado, here's an implementation that gives us enough:
IoRecord := Object clone do(
init := method(
self belongings := list
self hasOnes := list
self hasManies := list
self hasFews := list
)

appender := method(msg,
blk := block(
call sender doMessage(msg) append(call message next name)
call message setNext(call message next next)
)
blk setIsActivatable(true)
)

collector := method(
meths := call argCount / 2
waiter := Object clone
for(index, 0, meths-1,
waiter setSlot(call argAt(index*2) name,
appender(call argAt(index*2+1)))
)
waiter
)

belongs := collector(
to, belongings
)

has := collector(
many, hasManies,
one, hasOnes
)

curlyBrackets := method(
current := self clone
call message setName("do")
current doMessage(call message)
)
)
Since I'm a bad person and really enjoys metaprogramming, I had to get rid of most of the duplication the first implementation contained. Let's say it like this: the first version didn't have the collector and appender methods. And boy do they make a difference. This is real metaprogramming. Actually, these two methods are actually macros, almost as powerful as Common Lisps. Notice that the words we send in to collector doesn't actually get evaluated. This is one of the reasons we don't need to use symbols - we can just take the unevaluated messages and take their name. In the appender macro we're doing something really funky where we use setNext.

The final effect of that setNext call is that in something like "has many foobar", Io will not try to evaluate foobar at all. In fact, even before Io tries this, we remove the foobar message from the chain and inserts the next message instead.

Oh, right, and the Io lexer actually inserts a synthetic message called "curlyBrackets" when it finds curly brackets. The brackets themselves actually act exactly like parenthesis. You can try this out in Io by changing the closing parenthesis to a closed curly bracket instead - Io doesn't care. Your brackets doesn't have to match up in type, just in cardinality.

Of course, this is just the beginning. Io will blow your mind.

The thing I'm finding myself missing is a good literal syntax for hashes and lists. I'm thinking about implementing something for that. Also, using atPut is starting to get annoying. I want square brackets access. Shouldn't be too hard, since Io does the same thing with square brackets as it does with curly brackets. Almost, at least.

And btw, the Io standard library... Not sure what I think about it. I miss many things from the Ruby core library actually. Now, Io combined with the Ruby libraries, that might be fun?

6 kommentarer:

Anonym sa...

Personally, I find *Ruby's* core libraries quite lacking. I would find Io combined with Java's stdlib quite interesting. New JVM lang project anyone? :-)

Jean-Baptiste sa...

You might find the post one Objective-C Syntax interessting
http://www.iolanguage.com/blog/blog.cgi?beforeId=88

It was a revelation, when a discovered how messages are working in Io. For me it is a cross-over between Lisp macros and Smalltalk messages!

I implemented Smalltalk-like blocs syntax.
And somewhere on the net, I found Python list and dict creation syntax for Io List and Map.

It is quite fun.
But I have problem to implement write acces on List or Map, with bracket syntax.

eg: how to have Map updating like
m{"foo"} = "bar"?

Yurii Rashkovskii sa...

Ola,

I've tried to craft some kind of core extension library Protoext and rspec-like Protospec back in 2007.

It is somewhat outdated and messy, though.

You can find it at http://svn.verbdev.com/protonio/trunk/

Currently, it is sorta discontinued but I might be back to Io when I'll decide to port StrokeDB to Io (that could be exciting experience!)

Anonym sa...

Getting hash and array literal syntaxes to work in Io is really only a matter of a few lines of code. I don't have the code in front of me right now, but I believe a simple array literal syntax comes out to a single line of code... and the hash syntax comes out to three.

It really is ridiculously excellent.

(As a side note, I'm working on a little miniature framework in Io [termed Iota, aren't I clever?]... I hope you won't mind if I borrow the code you posted.)

Isaac Gouy sa...

Can you do the same thing in Lisaac?

Anonym sa...

Quite cool but isnt this less readable than the Ruby variant?