

Monday, April 23, 2007

RuPy 2007

the thicket by Steve took it from flickr (CC-NC-SA)

I've been too busy coding to write anything about it earlier, but a week and a few days ago I attended a Ruby & Python conference in Poznań - RuPy 2007.

I strongly prefer active participation, so I prepared a talk for the conference. My talk was about RLisp. I wasn't really sure if it would be well-received, as the conference was dominated by web developers, and back then RLisp didn't even have a web framework. As it turned out the reception was very enthusiastic, and many people seemed to be fascinated by the idea, even if just for the sake of geek points. What surprised me even more was that the very first person I talked with knew my blog.

Slides I prepared for the talk are available in ODP and PDF formats. The main point of my talk was that RLisp is at the same time Lisp with access to all great Ruby libraries, and Ruby with macros. There were few Lispers in the audience, but Lisp hype spread by Paul Graham and others seemed to have reached everyone. I made a bold claim that RLisp has more libraries than all other Lisps taken together. Ruby definitely does, thanks to Ruby gems, and RLisp is a fully-featured Lisp. The boldness was in my assumption that Ruby and RLisp code can seamlessly live together and naturally blend. I designed RLisp with this goal in mind, but it with so little real life testing maybe it's too early to say such things.

I think that integrating with Ruby runtime was the best possible choice. An obvious alternative from library count point of view would be Perl, but its semantics are so hideously complex that a language living in the same environment as Perl would have to be Perl, or else it would have severely restricted access to its libraries. I'm talking about Perl's semantics, not syntax. Things in Perl don't even know wheather they're equal to other things or not. Strings "00" and "0" are considered equal in some contexts, but not in others (they're ==, they are not eq, and the former is true while the latter is false). If any language were to work with Perl, it would need to support String-Number hybrids, scalar/list/void and some even more obscure contexts for all function calls, badly bolted-on OO system without even usable reflection, and it even would need to copy all ugliness of Perl references. I tried it once, it wasn't nice.

I'm not trying to bash Perl (at least not too much, it's such an easy target it wouldn't be much fun). It has many redeeming features like CPAN and focus on getting things done efficiently even if it meant breaking with holy traditions of programming language design. The last time I wrote about Perl here an anonymous reader left a truly insightful comment:
Saying "Perl is great at ..." is the same as saying "I don't know much about ...."

And that's the very point of Perl. You grab a package from CPAN, run some Unix commands, convert the data with a few one liners full of regular expression magic and you're done. There's not enough time for becoming an expert at everything. It's far more efficient to take advantage of someone else's expertise - in this case the the CPAN module's author's. Of course every now and then you get deep into something. One of the greatest things about Perl is that the community of Perl programmers developed strong traditions of sharing effects of their work, on CPAN and elsewhere.

Continuing the side note, Python seems rather unfriendly towards functional programming, and static-centric environments like JVM and .NET would most likely cause very severe impedance mismatch with a highly dynamic language like Lisp. I'm really impressed by how far JRuby guys were able to get. Code examples available online don't seem particularly drastic for code that crosses language boundaries, and between two established languages at that, with so little room for modifying either language to work better with the other.

Getting back to my RuPy talk, one of the greatest powers of Lisp are its metaprogramming facilities - having a very convenient representation for code, which can be robustly generated, transformed, and analyzed. Robust code generation is a far more important issue than most people realize. Four common bugs in run-time code generation - XSS, SQL injections, PHP includes, and format string attacks - are responsible for 40% of all security vulnerabilities (counting only vulnerabilities of known types). Unfortunately in Web 2.0 even HTML is code, highly security-critical code at that. Would you rather have robust and convenient macros for generating safe HTML, or pray every day that you never forget to escape dangerous input ? I don't want to sound bleak, but we all know for how long people had means of avoiding all buffer overflows, SQL injections, PHP include, format string, and many other classes of vulnerabilities, and didn't use them. Most likely many people will keep escaping HTML by hand even in 2020.

Returning from another side track, RLisp is Lisp with Ruby libraries, object system and so on, but it's also Ruby with macros. For some reasons languages with rich syntax like Ruby rarely get powerful macro systems, and even when they get some, like Dylan, Nemerle, and Lua, macros are very reluctantly used by most programmers. Only in Lisps macro use is widespread. I think the basic reason is that Lisp code can not only be generated easily, but albo transformed and analyzed. All Lisp syntax gets converted to a small core, and the core is all a metaprogrammer needs to deal with. It's certainly possible to imagine all Ruby syntax getting compiled to just literals, message sends, and a few control structures. Unfortunately real Ruby AST has 105 kinds of nodes in its abstract syntax tree, the set which is highly non-orthogonal, and it's difficult to transform the AST in any nontrivial way without accidentally breaking code semantics. If you feel like getting your hands dirty, SexpProcessor converts Ruby to something vaguely Lisp-like and back. I tried it once and it SexpProcessor felt much harder than Lisp macros. Just in case someone thinks I'm exaggerating, here's an example of Ruby magical behaviour. When a blocks is called with one argument, it expects multiple arguments (possibly optional, but |*args| doesn't cause magic), and the argument is an Array, it is magically expanded into a list of arguments.
irb> x="foo";{|a,*b| a}.call(x)
=> "foo"
irb> x=[1,2];{|a,*b| a}.call(x)
=> 1
I'd love to improve Ruby's elegance and hackability. If I had my way, I'd get rid of most of these irregularities in Ruby 2 (saving one or two keypresses for explicit * or really isn't worth the added complexity), together with protected, most of the private (global functions like Kernel#puts need to stay private for technical reasons, but many should be made public, especially commonly used metaprogramming methods like Module#define_method), class checks on UnboundMethod#bind (checking internal C representation is more than enough), and I'd get Object#become, Object#class= and other useful methods from evil.rb. I guess I'm not a particularly representative Ruby hacker, so maybe it's better for most that I'm not in charge of Ruby. ;-)

I really meant to write about the conference, but it turned into a post about RLisp. So on the conference ... people seemed to like RLisp. The obvious question I received after the talk was about web frameworks for RLisp. Back then there weren't any, the compiler was barely written a few days before and not even pubished, but I thought RLisp should be able to use Ruby on Rails without too much porting effort. And indeed just a few days later Brad Ediger created an RLisp plugin for Rails. Ruby has not only great libraries, but also great hackers.

And on RuPy 2007, the talk I liked most was "PyPy, the implementation of Python in Python". They're doing a lot of cool stuff with Python, I'd realy love to see similar things for Ruby. Some other talks I liked were "Python usage in Embedded Environments", "Domain-specific languages in Ruby", and "Hands-on! Wrapping FileMagic as a RubyGem". Between sessions, there were many cool hackers there, a lot of them involved in local Ruby User Groups (or suchlike). I think I'll follow their advice and check if there's something like that around.

No comments: