Io in Seven Languages in Seven Weeks
I've just finished week two of Seven Languages in Seven Weeks by Bruce Tate. Last week was Ruby where we dived into writing Domain Specific languages and meta programming. The Ruby chapter got me thinking about how I can use mixins in Java but one of the examples about meta programming left me confused.
This week was Io. Io is a prototype language. Instead of classes acting as templates for objects, the language starts off with one Object instance and the programmer can create new objects by cloning and adding "slots" (i.e. methods and variables). You can certainly emulate programming with classes but you don't have to.
The Io chapter continued with the theme of meta programming. The DSL example was interpreting a file format (that looked a bit like JSON). The interesting thing was that the program interpreted the contents of the file instead of parsing it. The curly brackets and colons of the JSON were set up as overridden methods and operators. The real magic would come if the DSL manipulated state and had control structures. I think I can see how to get there and I'm excited to write a simple interpreter in Io at some point in the future.
The meta programming example was even more intriguing. The program had to convert from a made up nested syntax (LispML) to another nested syntax (XML). LispML was interpreted as nested method calls but the catch is, just like in XML, you don't necessarily know all the tag names (i.e. method names) ahead of time.
The most fun part for me was stumbling across an incredibly simple solution to one of the exercises. I had to implement a new list creation syntax. Flat lists were easy with a loop but nested lists were a bit trickier. I did something that didn't work, commented out a bunch of code to test something which left me with the following.
squareBrackets := method( call evalArgs )
This was really just to help me work out something about how Io was executing my code but it turned out to be the answer to the exercise. It took me ages to work out why this worked but I think it is because evalArgs returns a list of the evaluated arguments. If one of the arguments is a [nested] list then a corecursion is set up when it is evaluated.
Here was my entire program for that exercise. It prints out an example in my new syntax and then the same example from the old syntax for comparison. I still can't get over how simple it was.
squareBrackets := method( call evalArgs ) ["A", "B", "C", [1, 2, ["Z", 5, nil, ], 3]] println "... is the same as ..." println list("A", "B", "C", list(1, 2, list("Z", 5, nil, list()), 3)) println
All of these examples were made easy by Io. The language has very little syntax (to the extent that loops and if statements are part of the libraries). However, everything can be overridden or replaced and the language elements can be combined in very expressive ways.
I certainly had fun with Io and it is a language I want to come back to. The lack of Syntax reminds me of Lisp and the expressiveness is reported to be similar. One thing I'll be looking out for is parsers or interpretors to do as mini projects. I think Io might be a good choice of language even if it is just a prototype before reimplementing in the project's main language.
29 September 2014