top | item 5010071

μLithp - a Lisp in 27 lines of Ruby

137 points| Peteris | 13 years ago |fogus.github.com | reply

98 comments

order
[+] mahmud|13 years ago|reply
Before this devolves into another farcical, Lisp arm-chair punditry: at 9 operators it's pretty much a kernel untyped lambda-calculus with pairs. And just that.

The implementation is as trivial as the concept is profound. The discussion should be about the lambda-calculus, not Lisp, which is a far more complex beast. And much less about Lisp dialects, Greenspun's, or the usual BS topics that invariably appear on any L-word thread.

[+] fogus|13 years ago|reply
The fun part that distinguishes an early Lisp from the pure lambda calculus is the mixing of strict evaluation and call-by-need. I had to hack that a bit by making a distinction between Procs created with proc vs those with lambda and by tearing apart function bodies. I forget how the original Lisp made the distinction. It looks like I need to re-re-re-re-re-read the Lisp 1.5 manual.
[+] fogus|13 years ago|reply
If I had a little more time I would have written the uLithp eval in uLithp. The stark simplicity of that never fails to take my breath away.
[+] bitops|13 years ago|reply
I think this is a neat project and a nice demonstration of how Lisp-influenced Ruby is.

However, whenever people post "Lisp in Ruby" stories, I always hope that it'll be a "Clojure in Ruby" implementation. I am surprised no-one has done it yet.

[+] pekk|13 years ago|reply
Apart from using the JVM, what is the advantage of Clojure over other Lisp dialects?
[+] jjtheblunt|13 years ago|reply
Why would anyone? Clojure runs on the JVM which supports native os threads, concurrent garbage collection, etc, for one thing. Different tools for different purposes.
[+] dschiptsov|13 years ago|reply
Don't call it Lisp.

Lisp begins with something like this:

  (defmacro when (test &body forms)
      `(cond (,test nil ,@forms)))
Without using underlying list structure, environments, lexical scoping and reader function it is just a lisp-like syntax.

Lisp without lists at its core is nothing but another clumsy language. Look at Clojure - what a mess.

[+] guard-of-terra|13 years ago|reply
What's the problem with Clojure?:

    (defmacro when
	  "Evaluates test. If logical true, evaluates body in an implicit do."
	  {:added "1.0"}
	  [test & body]
	  (list 'if test (cons 'do body)))
Moreover, what's the problem with μLithp?

    l.eval [:label, :second, [:quote, [:lambda, [:x],   [:car, [:cdr, :x]]]]]

    l.eval [:second, [:quote, [1, 2, 3]]]
Seems list-y enough for me. I have a feeling that you didn't actually read the article.
[+] quarterto|13 years ago|reply
Yeah, 27 lines. Unless you count the ~120 lines of sexpistol.

https://github.com/aarongough/sexpistol

[+] apl|13 years ago|reply
Syntactic transformation isn't really what it's about, is it? Parsing S-expressions into some internal representation is easily the most boring component of a working Lisp.
[+] joethephish|13 years ago|reply
Not sure what this proves exactly. For fun, here's a Javascript equivalent in 27, err, "lines":

    module.exports = function() {
        var atom = function(val) { return val instanceof Array ? false : true; };
        var env = {
            label:  function(sexpr, senv) { senv[sexpr[1]] = evaluate( sexpr[2] ); },
            quote:  function(sexpr, senv) { return sexpr[1]; },
            "==":   function(sexpr, senv) { return evaluate(sexpr[1], senv) == evaluate(sexpr[2], senv); },
            head:   function(sexpr, senv) { return evaluate(sexpr[1], senv)[0]; },
            tail:   function(sexpr, senv) { return evaluate(sexpr[1], senv).slice(1); },
            conc:   function(sexpr, senv) { return [evaluate(sexpr[1])].concat(evaluate(sexpr[2])); },
            "if":   function(sexpr, senv) { return evaluate(sexpr[1], senv) ? evaluate(sexpr[2], senv) : evaluate(sexpr[3], senv); },
            atom:   function(sexpr, senv) { return atom(sexpr[1]); },
            lambda: function(sexpr, senv) {
                return function(lexpr, lenv) {
                    for(var i=0; i<sexpr[1].length; ++i) lenv[sexpr[1][i]] = evaluate(lexpr[i+1], lenv);
                    return evaluate(sexpr[2], lenv);
                };
            }
        };

        var evaluate = function(sexpr, senv) {
            senv = senv || env;
            if( atom(sexpr) ) return senv[sexpr] !== undefined ? senv[sexpr] : sexpr;
            else return senv[sexpr[0]](sexpr, senv);
        };

        this.evaluate = evaluate;
    };
Example:

    var notlisp = require("./notlisp.js"); 
    var l = new notlisp();
    l.evaluate( ["label", "second", ["lambda", ["x"], ["head", ["tail", "x"]]]] );
    l.evaluate( ["second", ["quote", [1, 2, 3]] ] );
[+] sejje|13 years ago|reply
I want to learn a lisp variant. I'm okay in several scripting languages--Python, Ruby, PHP.

Any obvious choice? (If not, I imagine I'd like to learn the most widely-used)

[+] steveklabnik|13 years ago|reply
> I imagine I'd like to learn the most widely-used

Learn yourself some Clojure for great good.

[+] 6cxs2hd6|13 years ago|reply
Racket is another obvious choice.
[+] blue1|13 years ago|reply
Common Lisp is the obvious choice
[+] nine_k|13 years ago|reply
1993: «Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.» [1]

20 years later: for Ruby users, situation improves! :)

[1] http://en.wikipedia.org/wiki/Greenspuns_tenth_rule

[+] jrockway|13 years ago|reply
"Common Lisp" means the loop macro and CLOS, I think. And you guys still don't have those ;)
[+] tzaman|13 years ago|reply
Is it normal that I don't have the slightest idea what I'm reading here, or is it just me?
[+] nine_k|13 years ago|reply
If you don't know what Lisp is, and are a programmer, it's high time to go read about its basic principles.

Lisp is very simple and very powerful. Those who don't learn it are doomed to reinvent it, badly (cough XSLT cough).

[+] voidlogic|13 years ago|reply
I can't say for sure- but I would guess that it would be normal for a lot of HN articles to go over your head if you are not technical or lack a C-S degree.
[+] hk__2|13 years ago|reply
You should use Docco for your page, I find it much more readable: http://jashkenas.github.com/docco/
[+] dbyrd|13 years ago|reply
Cool project, but I think it's been done before in less lines: https://gist.github.com/4176087
[+] JasonFruit|13 years ago|reply
I've never understood the line-count craze some people get on. If it was that important, we'd all be using J or APL. Since we're not — in fact, almost nobody is — it must not be that critical.
[+] nine_k|13 years ago|reply
The linked gist has 40 lines (36 LOC); the article from the post boasts 27 lines.
[+] fogus|13 years ago|reply
That's based on an older version of uLithp that had a couple bugs.
[+] asimjalis|13 years ago|reply
Who’s up for implementing a μRuby on top of this μLisp?
[+] rrmm|13 years ago|reply
I love the economy of lisp implementations in dynamic languages. It's especially nice because you don't have to worry about the memory management.

On the otherhand, I've been interested in doing a lisp in low memory environments (uC's, etc). I've done a dialect in C using a semi-space garbage collector. But I'm curious if anyone's done any work on lisps in resource constrained environments.

[+] lopatin|13 years ago|reply
Really? Lithp? Not that I'm offended, but the pun seems unnecessary.
[+] pohl|13 years ago|reply
Is necessity a prerequisite for humor?
[+] dmcdougall_|13 years ago|reply
My one question is: Why?
[+] albertoavila|13 years ago|reply
It's weird that this question keeps coming out in several cool but not that useful HN posts.

Some times it's cool to do things just because you can, maybe he was bored or he always wanted to implement a lisp dialect.

It might not be that useful, but i'm sure that the autor had a blast doing it, otherwise he wouldn't have done it.