Hacker Newsnew | comments | leaders | jobs | submitlogin
Lisp in about 200 lines of Ruby (github.com)
92 points by r11t 92 days ago | 34 comments


18 points by lisper 92 days ago | link

Lisp in about 100 lines of Python:

http://www.flownet.com/ron/lisp/l.py

-----

1 point by mahmud 91 days ago | link

Please instruct your httpd to serve *.py files as "text/plain". Right now it's forcing a download. Win32 FF.

-----

1 point by lisper 77 days ago | link

It's being served as text/x-python, which I think is appropriate. So it's not the server that's "forcing" a download, it's your client. I suspect you're using FireFox, which seems AFAICT to be b0rken in this regard.

-----

8 points by MaysonL 92 days ago | link

A micro-Lisp compiler (to x86 executable) in about 100 lines of OMeta: http://www.vpri.org/pdf/m2009011_chns_mng.pdf

examples given which run about 70% of the speed of equivalent C code.

-----

1 point by sb 92 days ago | link

That's a nice one, albeit several simplifications are made in there (e.g. from a quick glance I could not find anything dealing with scopes [the prefixing with "_V_" for variables might probably hint at its non-existence]).

Nevertheless, impressive work from one of the most interesting projects around.

-----

5 points by gord 92 days ago | link

This makes me vocalize something Ive wondered about...

Whats the smallest subset of lisp which you need to implement in order to bootstrap the language? So, given this small subset, one can implement the rest of say scheme or common lisp or arc on top of.

I vaguely recall Sussman or Ebelson implementing car and cadr in terms of lambdas, for example.

It seems this should be quite modular, given so many implementations of lisp [ in javascript, PHP, etc now Ruby ]. So that new lisps might be easily brought up over this kernel.

-----

12 points by fadmmatt 92 days ago | link

The lambda calculus is Turing-complete, so the following language is sufficient to encode any computable function:

exp ::= var | (lambda (var) exp) | (exp exp)

Check out

http://matt.might.net/articles/church-encodings-demo-in-sche...

To see how to build numbers, booleans, lists, conditionals and recursion out of pure lambdas.

Even cooler is that these tricks work in languages like JavaScript too:

http://matt.might.net/articles/implementation-of-recursive-f...

-----

10 points by mdemare 92 days ago | link

According to pg, seven: (quote, atom, eq, car, cdr, cons, and cond)

Eighth paragraph: http://www.paulgraham.com/ilc03.html

-----

1 point by jmatt 92 days ago | link

Ya that should be the minimum required to bootstrap. I always thought the definitive minimum code for lisp was here:

http://lib.store.yahoo.net/lib/paulgraham/jmc.lisp

Or at least from a historic perspective.

-----

1 point by onewland 92 days ago | link

Quote as a predefined macro could be important though, in a Lisp, if you expect to be doing a lot of macro magic, and I don't see it here.

This is cool, though, and very readable; I know what all the code is doing with a quick glance. Cheers to the author.

-----

8 points by twilightsentry 92 days ago | link

Well, you can define pairs like this (as long as you've got lexical scope):

  (setq cons (lambda (x xs)
               (lambda (msg)
                 (if (eq msg 'car) x xs)))

  (setq car (lambda (xs) (xs 'car)))
  (setq cdr (lambda (xs) (xs 'cdr)))
That only uses 'setq, 'lambda, 'if, 'eq, and quoted symbols. That pretty much gives you the untyped lambda calculus, plus some side effects. Throw in the appropriate macros and you've got the core of Scheme and Arc, without any I/O or efficient data representations.

You can take this pretty far. Essentially, you just need 'lambda and application. You could use church numerals for numbers, monads for side effects, the y-combinator for recursion, etc.

-----

1 point by mahmud 92 days ago | link

Under which lisp implementation do those forms work?

The definitions for CAR and CDR are identical and they don't make much sense (set the symbol 'car to the value of a function that takes angument xs, which is a function, and applies that to the symbol 'car)

And the definition for CONS is equally opaque.

How would you use those functions, CONS, CAR and CDR?

Since you pretty much nailed the uses of church numerals, monads and the y-combinator, I know you know what you're talking about, it just that Lisp's semantics is confusing me (it has common lisp keyword SETQ but none of its behavior)

-----

1 point by twilightsentry 91 days ago | link

Oops; I meant to write those in CL, but I forgot about functions having their own namespace (been playing too long in a lisp-1). It should work in CL if you use FUNCALL.

If mquander's explanation isn't enough, you might look at it using terms from the OO world: CAR takes a pair object and calls its 'car method.

-----

1 point by mahmud 91 days ago | link

So you mean a meta-circular OOP implementation, where the hosting language already has car, cdr and cons. Alright.

I thought what you wrote was supposed to be a primitive implementation of the functions.

-----

1 point by twilightsentry 86 days ago | link

No, not really, unless you're running the code in a metacircular interpreter.

Imagine encoding the pair ('hello . 'world) into a function. You might say:

  (lambda (msg)
    (if (eq msg 'car) 'hello 'world))
You could get 'hello by applying that function to the 'car symbol, or 'world by applying it to anything else. Wrapping that in another function lets us generalise to any CAR/CDR.

  (lambda (car-value cdr-value)
    (lambda (msg)
      (if (eq msg 'car) car-value cdr-value)))

-----

1 point by mquander 91 days ago | link

Those look correct to me. The function returned by cons is just a switch that holds two values in a closure, and interprets what you pass it as a signal to determine which to retrieve. You could just as easily rewrite it to pass it 0 or 1 instead of car and cdr.

If you evaluate (car (cons 1 2)) and (cdr (cons 1 2)) on paper using substitution, you'll see how it works.

-----

1 point by dkersten 91 days ago | link

Exactly. SICP shows some similar code, though I don't remember which part..

-----

5 points by silentbicycle 92 days ago | link

http://www.c2.com/cgi/wiki?ImplementingLisp

Pair-structured memory, a (very simple) recursive-descent parser, eval, apply, read, print, a few mathematical primitives, not too much else. Once you have the bare bones, _The Art of the Interpreter_ provides a good skeleton. Don't worry about a garbage collector yet - either use a language with one (Scheme, Python, Lua, Ruby, OCaml, etc.) or just let it leak memory for now. (Garbage collectors aren't deep magic either, but one thing at a time, you know?)

Implementing a toy Lisp is absolutely worthwhile. It shouldn't take too long* , and it will teach you several deep things. Don't worry about making it efficient until you have it together enough to be useful. (If you want a mini language that's mature, just use Lua.) This is about discovery. Many interpreters for mini-languages don't need to be efficient, anyway. Who writes 20,k-long files in a DSL?

* Though if you've never done an interpreter at all, a half-assed Forth is even simpler. Either way, implementing half-assed Lisps, Prologs, Js, etc. is a great way to feel out a language. Worrying about getting it efficient upfront can be a red herring.

-----

3 points by procrastitron 92 days ago | link

Well, lambda calculus is Turing complete, so you could just stick with a call-by-value version of that. However, you'd want to add macros if you wanted to extend the syntax of the language. Since you mentioned car and cdr in lambdas, they'd be defined like this:

NIL = (lambda (x) (lambda (y) y))

CONS = (lambda (ar) (lambda (dr) (lambda (op) (op ar dr))))

CAR = (lambda (expr) (expr (lambda (ar dr) ar)))

CDR = (lambda (expr) (expr (lambda (ar dr) dr)))

-----

1 point by sb 92 days ago | link

Since nobody mentioned it, there is a very interesting paper among what are know the "lambda papers":

http://library.readscheme.org/page1.html

What you are looking for is answered in "The Art of the Interpreter, the Modularity Complex (Part Zero, One, and Two)", Steele and Sussman, MIT AI Lab Memo 453, May 1978.

http://repository.readscheme.org/ftp/papers/ai-lab-pubs/AIM-...

More LISP details can be obtained by the book "LISP in Small Pieces" by Christian Queinnec (1996), and an excellent description of the eval/apply functions can be found in "The Architecture of Symbolic Computers" by Peter M. Kogge, 1990.

Btw: to satisfy my more or less latent pedantism: The second SICP author is called Abelson not Ebelson.

-----

1 point by jacquesm 92 days ago | link

hehe, I had this open two tabs to the left of HN :)

http://www.faqs.org/faqs/lisp-faq/part1/section-6.html

-----

6 points by gord 92 days ago | link

Nice link.

Frustrating to have to login to ACM to read McCarthys - 'A micro-manual for LISP - not the whole truth'

Thankfully, found this hidden away at [pdf d/l link] : http://www.ee.ryerson.ca/~elf/pub/misc/micromanualLISP.pdf

-----

1 point by jacquesm 92 days ago | link

thank you, I'd been looking for that one :)

-----

2 points by statictype 92 days ago | link

I threw something like this together in python some time ago: http://bitbucket.org/statictype/code/src/tip/pylisp/

Not as elegant though.

-----

2 points by bodhi 92 days ago | link

I wonder if this says more about Ruby, or more about Lisp?

-----

2 points by z8000 92 days ago | link

Does lisp not support negative numbers? :)

-----

1 point by kidko 91 days ago | link

Fixed in 1.4.2... Thanks for pointing that out!

-----

1 point by stcredzero 91 days ago | link

X in (some small number) of lines of Y

This is generally an indicator that language_power(X) > language_power(Y)

-----

2 points by RiderOfGiraffes 91 days ago | link

I think you have that the wrong way round.

Taking a specific example, "Lisp in 100 lines of Ruby" means that once you have Ruby, you have Lisp. That means that anything you can do in Lisp can be done in Ruby, and hence Ruby is at least as powerful. That means:

X in (some small number) of lines of Y

implies

language_power(X) <= language_power(Y)

Exactly the opposite of what you said.

-----

1 point by gaius 92 days ago | link

Greenspun's Law in action ;-)

-----

2 points by mmphosis 92 days ago | link

Greenspun's Tenth Rule of Programming: "Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified bug-ridden slow implementation of half of Common Lisp." http://philip.greenspun.com/research/

-----

-4 points by Anon84 92 days ago | link

A upgrade to Ruby?

-----

8 points by tyrmored 92 days ago | link

http://img43.imageshack.us/img43/2413/johnmccarthyposter1.jp...

-----

2 points by pjonesdotca 92 days ago | link

Thanks for that. It's been my motivational poster for a while now.

-----




Lists | RSS | Bookmarklet | Guidelines | FAQ | News News | Feature Requests | Y Combinator | Apply | Library

Analytics by Mixpanel