Friday, July 15, 2011

Ruby magic

I learned ruby for my internet programming class a year or two ago. I built a pretty cool rails app, and generally liked the features, but never really came back to it. I definitely developed some holy-envy for some of the dynamic features and magic that it has.

In the Utah Software Craftsmanship Group last week, we decided to make our next book we read Seven languages in seven weeks. The first chapter in that book is Ruby, so I was super excited to dig in and learn more of the nitty-gritty. Somebody pointed out Ruby koans as a great way to learn ruby, and I must absolutely agree. In the first three files alone, I have learned tons of the intricacies of the language, but also been a bit confused on some others.

For example: this here was quite confusing until I did some research on stackoverflow.

array = [:peanut, :butter, :and, :jelly]
array[0,2]  => [:peanut, :butter] #OK!
array[2,0]  => [] #OK!
array[2,20] => [:and, :jelly] #OK!
array[4]    => nil #OK!
array[5,0]  => nil  #Looks good
array[4,0]  => [] #WTF??!?!?. 

as lines 6 and 7 are both out of bounds, it was dang confusing as to why they would be different. Luckily I'm not the first to have this question. Stackoverflow quickly answered my worries and helped make (a little) sense out of it. If you consider the index as the spaces between elements, array[4,0] starts just after the last element, but has nothing after it, while array[5,0] has no elements before or after it. Still not sure if I agree with that decision, but it makes some sense.

My favorite bit so far though is this:

firstName, lastName = ["John","Smith"]
firstName, lastName = lastName, firstName

I've always known about these compound assignments, and I rather like them when they are not abused horribly. I always thought though that it was sugar for:

firstName, lastName = lastName, firstName

#is same as

firstName = lastName
lastName = firstName

But that is not so. It does more magic than that because without caching the results the name would become "Smith, Smith", not "Smith, John". To really get the in place swapping effect something like this is required:

temp1 = lastName
temp2 = firstName
firstName = temp1
lastName = temp2

Obviously the language designers anticipated this use, and put a generous sprinkling of magic in. I could do more experiments in putting stronger side-effects in the right hand expressions, as now I am a bit curious if it is as simple as my last snippet, or if there is even more magic going on. Maybe I'll do that in the future.

Bottom line is: Ruby is fun. I'm learning a lot.

Tuesday, May 3, 2011

Metaprogramming in Piet

I have always loved the Piet programming "language". It allows for the encoding of programs into a colorful image that resembles abstract art. It is the perfect mating of my love for coding and my love for epileptic fit inducing colors.

Quite a while ago I had the idea to implement a BrainF**k interpreter in Piet. I got halfway through a prototype before it got too complicated and I lost interest in the project for a while. Fast-Forward a few months and I find that it has now been done, no less than three times. I thought it would be a really cool way to show off the power of the Piet language. Seeing as that has now been done, I need to up my ante a bit.

What other crazy, esoteric language could I implement? What better choice than Piet itself? I can't think of one. I realized that npiet, the standard Piet interpreter, can handle ppm files with absolutely no problem. That essentially allows me to interact with images as strings of text, without having to muck about with decoding and encoding difficult image formats (although that is technically possible). In order to get to the point of a Piet interpreter written in Piet I need some more tools first, so I don't rip my brains out. Hopefully I can reach that lofty goal someday.

One discovery that helped me a lot was this blog post about QuickPiet. It is a language to create and debug algorithms on the Piet stack without having to worry about the spatial complications of actually laying out an image. I made a quick Piet interpreter, and made a few additions of my own, like modifying the way the goto command works. It is definitely possible to programatically transform a QuickPiet program into a working image, but I found the result looks mechanical, and is generally less artsy and less fun.
I decided a first step in exploring Piet meta-programming would be to try and write a Piet program that generates another Piet program. I decided to make a program that will accept any string as input, and output a Piet program that will also output that string. Rather worthless, I know, but valuable as a proof that it is possible.

So I coded it up in quick piet until I was fairly confident that it worked. The good thing about quick piet is that is removes all of the space and layout constraints that the image requires, and lets you just worry about the algorithms. So I had about 600 lines of quick piet that appears to work. I wrote a normalization engine to separate it into distinct labels, make all implicit jumps explicit, and generally clean it up. I took the normalized output, and a graph of which blocks lead to which other blocks, and I proceeded to lay it all out by hand (starting over twice when I got waay too lost to continue), and finally I got this little masterpiece:

That program will read all input until it receives a two quotation marks. It will then generate a valid Piet program (as a PPm image) that will in turn output all of the text between the quotation marks. So if I give it the input "Hello, World!", It will give me the following as a result:
Which will of course output "Hello, World!".
It seems to be pretty highly scalable, although a bit slow. For instance, here is a program that generates the declaration of independence:
And here is a real gem. This one outputs a piet program that will in turn output "Hello, World". I would try and wrap it one more level deeper, but I'm afraid the universe might explode if I do so. The image size is something like 55000x116, so it doesn't render very well in a blog. If you want to download it it is here.