RLisp and language integration

Now that the importance of libraries is widely appreciated, most new languages make a point of having good ones. Usually they get them the easy way: they borrow someone else's, by being closely integrated with their host languages. At a minimum this usually means sharing the same data and being able to call code of the host language. That's all you need to borrow libraries, and it works fine. But integration can go further.

RLisp is a Lisp integrated into Ruby. The two languages already have virtually the same data model, so that part is easy. Calling Ruby code has a small complication: Ruby is based on message send, not on function call. RLisp deals with this by supporting both. In addition to the ordinary funcall form, it has a send function, for sending Ruby messages. And there's syntax to make it convenient: [a + b] reads as (send a '+ b).

Since most RLisp calls are to Ruby methods, send is actually much more common than regular funcall. So much so that my first impression, on reading some RLisp code, was that they were backward: the Form Without A Name should be send, and funcall should be explicit. This is not the traditional Lisp way, but is it a bad way?

defun would have to define methods instead of functions, which would also make it easier for Ruby to call RLisp. It would be tempting to impose Ruby's restrictions on Lisp names, to minimize the hassles when calling back and forth. The main annoyance would be the loss of high-order-ness: ordinary methods would no longer be suitable as arguments for high-order functions, which would require an explicit lambda (or an η-expanding function macro - or could this be implicit?) and funcall. But that's no worse than in Common Lisp - or more precisely, in Smalltalk or Ruby.

The resulting language would be unusually closely integrated with Ruby, by borrowing its namespaces and even its Form Without A Name from the host language. In fact, it wouldn't differ much from Ruby except in syntax. But since the motivation for RLisp appears to be "Ruby with macros", this similarity is not a bad thing. If RLisp became essentially an S-expression syntax for Ruby, maybe with the rough corners cleaned up, that wold probably increase its utility, because it would be easier for users to move between the two languages.

One could make this even easier by further integration: defining a syntax for RLisp which looked exactly like Ruby, but read as S-expressions. The result would be a dialect of Ruby which happened to support macros. Not what either Matz or taw have in mind, perhaps, but it might be the easiest way to add macros to Ruby.

3 comments:

  1. One could make this even easier by further integration: defining a syntax for RLisp which looked exactly like Ruby, but read as S-expressions. [This] might be the easiest way to add macros to Ruby."

    What a great idea. It would also be useful for those of us who are still (despite all the good arguments) put off by all the brackets in Lisp. (In my case, in case you're interested, the problem is not reading the brackets but typing them.)

    ReplyDelete
  2. I wanted send to be separate from funcall because I really disliked the way CLOS is doing OO, and I really liked the Ruby way.

    As for identifiers Ruby lets you call your methods almost anything, except its parser won't let you use them. So if you have method "oh-my-god", then unfortunately it can only be called by obj.send(:"oh-my-god", args), not obj.oh-my-god(args).

    So having a choice of no allowing dashes etc., allowing them and making Ruby->RLisp calls somewhat harder,
    and mangling them to fit Ruby standard syntax (oh__DASH__my__DASH__god or automatic dash to underscore conversion or something like that) I took the second option. If I used mangling all metaprogramming methods would have to use mangled method names, so you'd need to manually mangle to metaprogram in RLisp, and that would be very very bad.

    Not allowing dashes etc. would be reasonable too, but there's not that much win.

    As for S-expressions for Ruby, it has a lot more rough corners that you think. Interactions between various pieces of its syntax and semantics would make it really difficult to write robust macros. One example - class ... end and def ... end create completely new lexical scope, so you cannot define classes/methods that close over something in the outer scope, you have to use lambdas and define_method - but lambdas and define_method have different semantics when it comes to arguments unpacking, class methods and other things.

    There's a library for S-Expressions in Ruby already, but it's not really that useful.

    ReplyDelete
  3. In T, Lisp-style functions are just methods on Object. I always thought that was very pretty.

    ReplyDelete

It's OK to comment on old posts.