Lisp in 32 lines of Ruby
Playing around yesterday during lunch.1
[sourcecode lang=”ruby” gist=”1677501″]
You are being redirected.[/sourcecode]And the LISP ecosystem has been enriched — much like manure to the garden.

uLithp Github repo — fork and reduce!
:F
this post was discussed on various posts and podcasts including: Ruby5, HackerNews, and Ruby Inside. BTW, this is now 2927 25 lines.
-
I also made some mods a few minutes after posting to reduce the count to 32 lines. Inspired by http://www.brool.com/index.php/the-tiniest-lisp-in-python ↩
38 Comments, Comment or Ping
Sebastian Galkin
Cool, now you can use it in ‘any sufficiently complicated Ruby program’
Jan 25th, 2012
Sam Aaron
Beautiful!
The only part I struggle to understand is the last line in apply:
Is this possibly the start of you adding user defined functions? Or am I totally missing something obvious?
Jan 25th, 2012
fogus
@Sam
Yes, that handles the user-defined functions case. That line actually has a bug, it should use
flatten(1)
. I’ve updated the code with an example definition of:second
. Basically that line, builds a function-specific environment.Jan 25th, 2012
Jaskirat
Nice! Reminded me of Peter Norvig’s post on the same. http://norvig.com/lispy.html
Jan 25th, 2012
Sam Aaron
@fogus Ah, makes complete sense – especially now there’s an example.
Again, beautiful work :-)
Jan 25th, 2012
fogus
@Jaskirat
I’ve read that post a few times. Love it! His Lisp has the advantage of providing a fuller experience with a REPL. It’s more inline with my Lithp impl. http://fogus.me/fun/lithp
Jan 25th, 2012
Jaskirat
Yeah, I noticed that this one doesn’t have a repl like experience, but hey it’s 32 lines and pretty darn succinct at that! :)
Excellent! lithp seems to be sprinkled with more than generous documentation. Let me check it out right away.
Jan 25th, 2012
Hannes
This should be called “risp” and pronounced the way Matz would pronounce it.
Jan 25th, 2012
Tim McCormack
Where does :lambda get introduced? Is it just being unwrapped as an unrecognized symbol and using Ruby’s own lambda?
Jan 25th, 2012
fogus
@McCormack
It’s a trick. That is, the env can hold atoms, lambda (i.e. callable things), and “something” else. This “something else” is assumed to be a Lithpy lambda of the form
[:lambda, [<args>], <body>]
. See my example defining:second
to see how to use it. The cool part is you do not need to use:lambda
… you could use any name you want and still achieve lambda-ness.Jan 25th, 2012
Carin Meier
Very nice :)
Jan 25th, 2012
Sean Nilan
Very cool!
One thing I noticed is that the above doesn’t allow for closures though. Changing the following line should work:
self.eval @env[fn][2], Hash[*(@env[fn][1].zip args).flatten(1)]
toself.eval @env[fn][2], ctx.merge(Hash[*(@env[fn][1].zip args).flatten(1)])
Jan 25th, 2012
Mike
I’m suddenly feeling very dim because I’ve never seen the |(a,b), _| syntax. I presume that the underscore is something like “Any number of arguments”?
My google-fu has turned up nothing helpful. Anyone have a link to an up-to-date online reference for Ruby 1.9 that covers this?
Jan 25th, 2012
Jesse Cooke
@Mike, _ is a convention used when you don’t really care about that variable. So that block expects 2 variables: a pair (a,b) and another variable that the author doesn’t care about, _. You see it more often in functional languages. I used it here (http://j.mp/zrj5IA) when implementing Float#round in Rubinius for 1.9 mode. I didn’t care about the first return value of Math.frexp(self), only the second.
Jan 25th, 2012
fogus
@Mike
My use of
_
is just to indicate that I don’t need to use that variable in the body of the block. There is nothing special about that name as far as I know.Jan 25th, 2012
Sam Dalton
The inevitable conclusion of this is to write it in 140 characters :D
Jan 25th, 2012
Mike
@Jesse Cooke & @fogus
WHEW! I’m glad that’s “just” an idiom, though I still don’t think I’m going to recover my Ruby Confidence (TM) for at least a week.
Jan 25th, 2012
pabloPXL
Congrats for such a compact implementation! I reproduced it in javascript and earned 1 line! :D
gist -> https://gist.github.com/1679611
Jan 25th, 2012
Justin Bailey
Very interesting. How would you implement a function like ‘const’? E.g., “lambda { |x| lambda { |y| x }}”?
Jan 25th, 2012
Petrik de Heus
Managed to get it down to 21 lines with some cheating. https://gist.github.com/1679766
Jan 25th, 2012
Petrik de Heus
Oops, deleted it by accident: https://gist.github.com/1680015
Jan 25th, 2012
Uncle Demotivator
What th… Oh! This is madness, dude. But it is also unspeakably cool, so it’s OK ;)
Jan 26th, 2012
Chris Umbel
This is exactly why I love both lisp and Ruby. Terrific native data structures like Ruby arrays & hashes and a beautifully simple language design like lisp s-expressions summarized in a short code example. I’m teary-eyed!
Jan 26th, 2012
Russ Olsen
An S-Expression (i.e. LISP) parser in 32 lines of Ruby:
https://gist.github.com/1684926
:-)
Russ
Jan 26th, 2012
Russ Olsen
And to finish the thought, a REPL that uses my s-expression parser and Fogus’ code above:
https://gist.github.com/1684947
R
PS in 12 lines of code.
Jan 26th, 2012
Fadi
Wowwww! I’m in love with LISP, it’s the best programming language ever, and now I’m playing with Ruby a bit… It’s amazing how much Ruby is powerful…
Maybe you can enhance your code to accept something like cadddr, cddaaar, etc… (they’re all a mix of car and cdr in any case).
Jan 27th, 2012
Petrik de Heus
Managed to simplify it to 17 lines and added specs :) https://gist.github.com/1680015
Jan 28th, 2012
theldoria
I also stumbled across the |(x,y),_| syntax, not for the _, but for the braces. I can not find any documentation about them, however, they seem to change the behaviour in some repect. For example, compare the following returned values: irb(main):105:0> ->(&b){b.parameters}.() {|(a)|} => [[:opt, nil]] irb(main):106:0> ->(&b){b.parameters}.() {|a|} => [[:opt, :a]]
I also found a usage for the braces with Hash iterator: irb(main):109:0> {key: :value}.map {|(a)| a} => [:key] irb(main):110:0> {key: :value}.map {|a| a} => [[:key, :value]]
Is that behaviour desired? Documented anywhere? Or is it experimental?
Jan 29th, 2012
theldoria
Ah, now I know what it was: the implicit splat with a nested level:
irb(main):049:0> [[:a, :b]].map {|x| x} => [[:a, :b]] irb(main):050:0> [[:a, :b]].map {|(x)| x} => [:a]
Jan 29th, 2012
Joel Gluth
Comments #1 and #8 both made me burn myself laughing while trying to drink soup. Great post, and actually (now that I think of it) a pretty decent advertisement for Ruby.
Feb 21st, 2012
parttimenerd
I like the way you wrote this tiny parser, it’s really cool, so I wrote the a tiny parser of brainf**ck, based on your LISP code (see https://github.com/parttimenerd/tiny-bf).
Sep 23rd, 2012
Aron Griffis
Seems like you can kill another line with this: return ctx[sexpr] || sexpr
Oct 30th, 2012
tamouse
Oh, Hats off to You!! This is absolutely awesome! Very well done. :))
Jan 4th, 2013
Sam Umbach
@fogus: What’s the motivation for ignoring the first element of the array [:lambda, [], ]?
Jan 27th, 2013
fogus
@sam
I just couldn’t figure a way to enforce that symbol without adding a line. Suggestions welcomed.
Jan 27th, 2013
Maria
Thank you for the articles, very useful! I’ve started learning LISP more then year ago, but gave up it soon. I had a lot of problems with choosing framework and with it’s installation. Recently I read your guide which helped me to setup emacs with slime correctly!
Oct 19th, 2018
Kate
Thanks for such a nice information. We have been using this languages since long but still we don’t know the creator of that language but as all creators are mentioned here so its easy for us to know that.
Nov 24th, 2018
Katrina
This is exactly why I love both lisp and Ruby. Terrific native data structures like Ruby arrays & hashes and a beautifully simple language design like lisp s-expressions summarized in a short code example. I’m teary-eyed!
Nov 27th, 2018
Reply to “Lisp in 32 lines of Ruby”