9 ms·
μLithp - a Lisp in 27 lines of Ruby
- tzaman 14y agoIs it normal that I don't have the slightest idea what I'm reading here, or is it just me?
- nine_k 14y agoIf 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).
- jjtheblunt 14y agoSo true re XSLT etc.
- bbx 14y agoI'm not exactly a programmer. Could you explain how Lisp can be simple and powerful?
- _sh 14y agoA lisp program's code can be manipulated by the program itself. Lisp programs are self-aware. It's a bit mind-bending, but from this simple foundation comes the power to easily create and assimilate ever higher-level abstractions (which is what programming is really all about). Parts of a program can be used as a template for creating other parts as needed. You can assemble these parts and shape the language into something very specific for the problem you are solving. Other languages come part-way, but don't fully embrace this concept (code=data) because it has a cost: it seems difficult to learn because it requires a change in thinking; it seems difficult to write (and read) because you're directly encoding a data structure and it all looks a bit 'samey'. Where other languages have 'shapes' in their code that give the trained eye an indication of what the code is expressing (such as for...do loops and indentation), lisp code has one shape: a tree [1]. I don't know if I've reached beyond your understanding. I haven't meant to, but the takeaway should be this: lisp programs are self-aware and can operate on themselves because a lisp program is just data like any other. This makes it easy for the interpreter/compiler (hence 'lisp in x lines of code'), but more difficult for the programmer (at first). As with all things, practice and familiarity overcome these initial difficulties. It is worth it. [1] http://en.wikipedia.org/wiki/Tree_(data_structure) http://en.wikipedia.org/wiki/Tree_(data_structure)
- bbx 14y agoThanks for your explanation. I know a bit about programming but it only comes down to PHP or jQuery variables, functions, and loops. Lisp looks very interesting and I had already heard about it (through pg obviously). It might just be the language that will fascinate me if I ever took programming more seriously.
- barik 14y ago> code=data This is a really good explanation, and I just want to add a small bit to it, having had to use Lisp as a primary language quite recently due to some AI projects in cognitive modeling. One thing that I've found personally is that by understanding Lisp, you can essentially gain a deep understanding of every __other__ programming language. If I had the chance to do my entire Computer Science career over again, I would have learned Lisp as my first language, and then compared every other subsequent language against it. Because of the deep symmetry between code and data, it is one of the easiest languages for implementing and re-implementing programming language features on top of.
- gpcz 14y agoChess has relatively simple rules (initial board layout, legal moves, win condition, some fanciness with en passant and castling, etc), but the game is endlessly complex, and there are more boards than a human being could ever see in his or her lifetime. Lisp is similar in that regard.
- manveru 14y agoI'd say comparing it with Go might be easier, it has 8 rules, and can be played on a board of any size. I like to compare that to the 10 commandments in "The Little Schemer" or the 9 functions in μLithp.
- troels 14y agoIt would be more precise to say minimalistic, rather than simple.
- gruseom 14y agoCould you explain how Lisp can be simple and powerful? You've asked a profound and important question, not so much about Lisp as about systems in general. A system is simple when it doesn't have many pieces. But if the pieces it starts with are very general, and the system provides good ways to put them together, then you can build complex things out of a tiny initial set. Think of a classic Lego set. There are only a few "primitives" (different kinds of Lego piece). And there is an easy and standard way to put them together. Starting from these simple initial conditions you can construct very complex structures. Lego is simple and powerful at the same time. How do you build a complex Lego structure? You start by putting a few pieces together to form a cluster. And then you make a second cluster and you join the two clusters together to make a third, and so on. The critical property here, the reason why Lego stays simple all the way, is that the "operation" you use to join two clusters is exactly the same operation you use to join two individual pieces – you interlock their knobby bits. In this sense your clusters are still "Lego pieces", just custom ones that didn't ship with the initial set: they still have knobby bits suitable for joining up with any other piece. This quality is sometimes called "regularity", meaning that the initial properties of the system are preserved as you put things together into ever more complex forms. Suppose that weren't the case and that each time you reached a certain level of complexity you had to learn a new technique for building further. That would make Lego much less simple-and-powerful. You could still build complex things, but it would be harder and more complicated. And there would probably be a bunch of things that, while you technically could build them, it would be so hard and complicated to do that nobody would bother. Programming languages also consist of a set of initial pieces and ways of putting them together. But most languages don't have the quality of regularity that Lego has. Their initial pieces aren't universal enough to get away with having only a few, so they need many more of them, and that means they're not simple, and that means they're not simple-and-powerful. There are, however, a few programming languages that do have this regularity. Lisp is one of them. Smalltalk, Forth, and APL are others. The really interesting thing is that this kind of simple-and-powerful system enables you to do qualitatively different things as complexity grows — not because other languages can't do them, but because they make it too much trouble to bother. For example, because Lisp programs are Lisp pieces in the same sense that Lego structures are Lego pieces, it's easy to write Lisp programs that take other Lisp programs as their inputs and do meaningful things with them. That is a powerful technique, and because it's so easy, Lisp programmers exploit it heavily, more than is practical in most other languages, and that means they can do more with less. This quality of simple-and-powerful is in my view very important and underutilized for managing software complexity. We don't yet understand it all that well, because the programming languages that achieved dominance to date don't have it.
- billsix 14y agohttp://mitpress.mit.edu/sicp/ http://mitpress.mit.edu/sicp/
- voidlogic 14y agoI 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.
- tzaman 14y agoI do lack a C-S degree, however I've been a PHP developer for years and recently switched to Ruby, or more specifically RoR.
- bcoates 14y agoThis video course should explain the whole situation and more and is targeted at undergrads with essentially no background knowledge: http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/ http://ocw.mit.edu/courses/electrical-engineering-and-comput... Ignore the hairdos, it's from the 80s, the information itself ages better than the fashion.
- tzaman 14y agoVery useful, thanx!
- wonderzombie 14y agohttp://www.defmacro.org/ramblings/lisp.html http://www.defmacro.org/ramblings/lisp.html I have been playing with a Clojure, a Lisp dialect, for six or so months (it seems so much longer), but this was still really interesting to me.
- bitops 14y agoI 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 14y agoApart from using the JVM, what is the advantage of Clojure over other Lisp dialects?
- nine_k 14y agoJavascript as a possible compile target (https://github.com/clojure/clojurescript https://github.com/clojure/clojurescript)? Optional static typing (a la Qi) is being implemented, too.
- mtrimpe 14y agoIt's default usage of purely functional data structures and a rich concurrency model (STM, actors and more.)
- glogla 14y agoI'm not sure about objective advantages (and note that I'm familiar only with Clojure, Common List and as much Scheme as there is in SICP), but Clojure feel kind of tight to me. In CL, array, hashmap, set and list have completely different interfaces, while in Clojure the interface is not only very similar (and nice, for example vector is also method to access itself), but allow you to use the same code to access different storage types. Clojure also felt more functional than CL. Clojure also felt more comfortable in ways such as distributing code, getting libraries and things like that, it's visible that the world of development tools have moved on since the CL of 1985.
- wonderzombie 14y agoI am mostly familiar with Clojure as opposed to CL (have read a bit of Practical Common Lisp) or Scheme (have read a decent chunk of SICP), but there are some pieces here and there. Clojure is immutable by default, which is a pretty big difference AIUI. It also has a pretty strong emphasis on concurrency, including a bunch of primitives for such. It uses STM. A bunch of data structures are first class, like vectors, maps, and sets. There are literals for each of them, and you can use maps and sets as functions. You can also use keywords as functions against maps to e.g. retrieve the :name value from a list of maps: user=> (map :name [{:name "foo"} {:name "bar"}]) ("foo" "bar") Lambda literals are nice: user=> (map #(* 2 (inc %)) [1 2 3]) (4 6 8) They can take multiple args (e.g. %1 %2) instead of just %. Threading macros are somewhat closer to Haskell's syntactically straightforward function composition: user=> (->> [1 2 3] ;; this'll be the last arg passed to each fn below #_=> (map #(* 2 %)) #_=> (map inc)) (3 5 7) I'm sure there's more. Maybe a more experienced Lisper can chime in.
- nine_k 14y ago1993: «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 http://en.wikipedia.org/wiki/Greenspuns_tenth_rule
- jrockway 14y ago"Common Lisp" means the loop macro and CLOS, I think. And you guys still don't have those ;)
- wonderzombie 14y agoDon't forget the corollary: https://en.wikipedia.org/wiki/Greenspuns_tenth_rule#Morris.27s_corollary https://en.wikipedia.org/wiki/Greenspuns_tenth_rule#Morris.2.... :)
- dbyrd 14y agoCool project, but I think it's been done before in less lines: https://gist.github.com/4176087 https://gist.github.com/4176087
- fogus 14y agoThat's based on an older version of uLithp that had a couple bugs.
- nine_k 14y agoThe linked gist has 40 lines (36 LOC); the article from the post boasts 27 lines.
- JasonFruit 14y agoI'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.
- LiveTheDream 14y agoIt's just supposed to be fun, challenging, and a learning experience.
- stcredzero 14y ago> If it was that important, we'd all be using J or APL. Fallacy. You haven't eliminated the possibility that line count is important, but also that too few lines is also bad. It's widely understood that line count, even though it's a hazy and exploitable metric, is indeed important. It's also widely understood that being too terse is as bad as being too verbose. Time to brush up on some old-school CS curriculum books. (Mythical Man-Month? I'm not sure if the above is discussed there, but it's a good place to start.)
- JasonFruit 14y agoI haven't proved anything, true — I was illustrating a point with an example, not writing a series of syllogisms. What I meant to do was to direct the reader to the broader idea that our language usage doesn't suggest that line count is a pressing concern past a point that allows for considerable verbosity. Now, wasn't it more enjoyable the way I put it first?
- hk__2 14y agoYou should use Docco for your page, I find it much more readable: http://jashkenas.github.com/docco/ http://jashkenas.github.com/docco/
- judofyr 14y agoOr, well, Rocco in this case: http://rtomayko.github.com/rocco/ http://rtomayko.github.com/rocco/
- codemac 14y agoAh, but this is org-mode! He can evaluate his code inside his editor! Grumble rah rah emacs! He could probably set up his publish templates to accomplish the same formatting. (huge org mode nerd, checking in) --- EDIT --- And what do you know, Eric Schulte already implemented this for org mode! It's checked in under contrib. Instructions: http://eschulte.me/org-docco/org-docco.html http://eschulte.me/org-docco/org-docco.html Org source: http://orgmode.org/w/?p=org-mode.git;a=blob_plain;f=contrib/scripts/org-docco.org;hb=HEAD http://orgmode.org/w/?p=org-mode.git;a=blob_plain;f=contrib/...
- fogus 14y agoI'm quite familiar with Docco having helped implement something similar for Clojure.[1] I wanted to use org-mode. Sorry it's not readable. [1]: http://github.com/fogus/marginalia http://github.com/fogus/marginalia
- draegtun 14y agoRelated: http://news.ycombinator.com/item?id=3511100 http://news.ycombinator.com/item?id=3511100 Above was HN submission from early last year, Lisp in 32 lines of Ruby, which was the original blog post of this code by Fogus.
- quarterto 14y agoYeah, 27 lines. Unless you count the ~120 lines of sexpistol. https://github.com/aarongough/sexpistol https://github.com/aarongough/sexpistol
- apl 14y agoSyntactic 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.
- fogus 14y agoMy s-expression reader is 34 lines. https://github.com/fogus/ulithp/blob/master/src/reader.rb https://github.com/fogus/ulithp/blob/master/src/reader.rb A minimal Lisp (the bare minimal) does not require sexprs though.
- gruseom 14y agoA minimal Lisp (the bare minimal) does not require sexprs Really? Why not? I realize that McCarthy's original spec used recursion equations, but that notation is more complicated than s-expressions.
- krosaen 14y agorelated: Norvig's implementation of lisp in python: http://norvig.com/lispy.html http://norvig.com/lispy.html
- lispm 14y agoNorvig's implementation of Scheme in Lisp: http://books.google.de/books?id=QzGuHnDhvZIC&lpg=PA756&vq=scheme+interpreter&dq=Paradigms+of+Artificial+Intelligence+Programming&pg=PA753&redir_esc=y#v=onepage&q=scheme%20interpreter&f=false http://books.google.de/books?id=QzGuHnDhvZIC&lpg=PA756&#...
- draegtun 14y agoAlso a Scheme implementation written in Perl by Bill Hails - Exploring Programming Language Architecture in Perl: (book & code) - http://billhails.net/Book/ http://billhails.net/Book/ | Previous HN post - http://news.ycombinator.com/item?id=1747132 http://news.ycombinator.com/item?id=1747132
- meric 14y agoplug Once upon a time I wrote a lisp to lua compiler in 100 lines. https://github.com/meric/l2l/commit/a530f0133e002c3981937d21b24d552041008181 https://github.com/meric/l2l/commit/a530f0133e002c3981937d21... It's now over a thousand. https://github.com/meric/l2l/ https://github.com/meric/l2l/
- dmcdougall_ 14y agoMy one question is: Why?
- albertoavila 14y agoIt'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.
- fogus 14y agoWhy not?
- fogus 14y agoBut really the "why" is that it was fun. Also, it was my submission to the PLT Games. http://pltgames.com/competition/2012/12 http://pltgames.com/competition/2012/12
- redler 14y agoTo learn.
- asimjalis 14y agoWho’s up for implementing a μRuby on top of this μLisp?
- mahmud 14y agoBefore 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 14y agoIf 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.
- fogus 14y agoThe 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.
- lopatin 14y agoReally? Lithp? Not that I'm offended, but the pun seems unnecessary.
- sejje 14y agoI 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 14y ago> I imagine I'd like to learn the most widely-used Learn yourself some Clojure for great good.
- sejje 14y agoLight Table implements clojure, right? Then I can get busy with that ahead of time (I've been sweating a Python release)! Thank you.
- steveklabnik 14y ago> Light Table implements clojure, right? Yep! Happy hacking: learning some Lisp variant is a great day/month/year/lifetime in any programmer's life.
- 6cxs2hd6 14y agoRacket is another obvious choice.
- chimeracoder 14y agoClojure has its selling points, but if you're learning Lisp for fun or intellectual gain, I'd definitely recommend Racket.
- blue1 14y agoCommon Lisp is the obvious choice
- rrmm 14y agoI 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.
- dschiptsov 14y agoDon'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 14y agoWhat'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.
- dschiptsov 14y agoThere isn't any problem with this Clojure macro. If you would like to see problems with Clojure, take a look at keep function in core.clj. There is a classic function: (define (filter fn l) (cond ((null? l) '()) ((fn (car l)) (cons (car l) (filter fn (cdr l)))) (else (filter fn (cdr l))))) The point is that stuffing more data-structures into a lisp ruins it. Somehow switching to the prefix notation and adding parenthesis doesn't transform Java to Lisp. For the second piece of code - are't semicolons and comas somehow redundant? Moreover, what is the point of writing something this way?
- vmind 14y agoNot sure what the objection to the keep function is, other than maybe the performance optimisations that chunked sequences allow. Taking away the chunked consideration gives something like: (let [x (f (first s))] (if (nil? x) (keep f (rest s)) (cons x (keep f (rest s))))) Which looks almost identical (with differences, as you seem to be giving filter rather than keep, which are different functions). The library function COULD be defined like this, it just wouldn't be as fast.
- joethephish 14y agoNot 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]] ] );
- deleted 14y ago[deleted]