Hacker News new | past | comments | ask | show | jobs | submit login
On Lisp (paulgraham.com)
125 points by luu on Aug 25, 2013 | hide | past | favorite | 104 comments



Instructions for printing it with lulu: http://www.lurklurk.org/onlisp/onlisp.html . I did this about a month ago and the copy I received was great.


I also printed it on Lulu several years ago - worked great.


Just tried to do this, and found that the cover image size doesn't seem accurate anymore. What did you do for the cover?


Really? I just made the book and ordered it last night and the cover (the 13MB png) looked like it lined up exactly. The first cover-uploader had a 10MB limit but the other worked fine (not the design-your-own, the 1-piece-cover-designer).


Turns out I was trying to get a hardcover. It worked just fine with a paperback.


The link to the lulu's book is not working for me... Can you please share it?


I've been meaning to read this for quite some time, but I've been told that the idioms and coding style is outdated, or not very good. This isn't going to stop me, but I'd like to know from some experienced lispers what problems I might encounter. Especially as I'm pretty inexperienced myself. I definitely won't know what to look for.


It is written to a prior version of the Common Lisp standard, and with a very old-fashioned style. You'll see calls like (REPLACA pair val) instead of (SETF (CAR pair) val) and so on. That will irk you if you are a Common Lisp hacker already. If you're not, just keep in mind that good CL style results in somewhat prettier code than that in On Lisp.


I tried reading this back when I started writing lisp- and it's been a year since. I know I got stuck when I couldn't find functions in slime with C-d C-h, and just assumed that they were defined by the book. This is much more reassuring.


REPLACA and its cousin REPLACD are part of the Common Lisp standard, but they are indeed something you should probably never see. Even for mapping across a structure or something I'd prefer to write my own inline function. They along with TERPRI (newline) are the ugliest function names of CL.


The book still works even if you assume every specific line of lisp in it is hopelessly outdated (I wouldn't know, I'm not a lisp programmer).

Suppose you were looking at lisps and got around to the part where you realize homiconicity makes it possible (and/or sensible) to have language facilities like syntax macros. What would such a facility look like in practice? What would it be used for and how? What could go wrong defining it or using it? That's the sort of thing that's covered in On Lisp and it makes it an interesting read for any sort of programmer, rather than just a lisp one. The book is short, without being incomprehensibly dense, you get easily get a feel for it from just browsing the PDF for a bit. And the concepts being presented are a lot easier to digest than, say, 'what if your language was really good at category theory'.


The book uses common lisp, so you'll need to know how to read that. Otherwise I think you should be fine. It's not a book about idioms or coding styles, but about thinking in lisp. If the code examples are confusing, it'll be because you're not sure what something means in common lisp or because you're learning new ideas, not because the code is poorly written. So, if you treat it as a book of ideas as opposed to "teach yourself x language in 24 hours", you should be OK :)


However, if you ARE looking for a "teach yourself Lisp in 24 hours" type of book, you could do a lot worse than "Practical Common Lisp" by Peter Seibel (http://www.amazon.com/Practical-Common-Lisp-Peter-Seibel/dp/...).


PCL is also available for free at http://www.gigamonkeys.com/book/


Another rec: Land of Lisp - http://landoflisp.com/ - is also very good if you're looking to get up to speed with the absolute bare bones, so to speak, fairly quickly.



I'm not an experienced lisper, but onlisp lacks what sicp describe when talking about abstraction barriers/layers. Also too much mutation. There's good content (macro, logic programming, patterns) but you need to detach from low-level lisp hackery.


Tain't Scheme---err Racket, 'tis Common Lisp, warts and all...


From a clojure perspective it is somewhat true. The clojure community tried to avoid macros as much as possible and doing to much with macros should be avoided.

There are of course still tons of examples in the book that are very useful for clojure as well.


I think both On Lisp and ANSI Common Lisp from PG are good books.

The only criticism I have is that PG does not write code in "Common Lisp style". It's often more of Scheme with Common Lisp.


Any advice or link for someone new to Lisp like me? Also, is there any good open source project using Lisp that I can fork/download and tinker with? I'm still having problem wrapping my head around Lisp. Even implementing some simple algorithms in Lisp took some time for me.

I think this is due to the fact that I learned to program in C. Nowadays I work mainly with python/ruby/javascript, but I got the feeling that I could improve myself as a programmer by learning Lisp.


Something that helped me (even though I didn't plan it that way) was switching over to emacs for coding and extending it using elisp. Suddenly, whenever I felt that there was a function or enhancement that I'd like the editor to have, I could implement it right away elisp and use it right away. You don't even need to create a plugin file or anything, you just hop over to the scratch buffer, write your function, evaluate it, and you extended the functionality. It is so easy that I do it all the time now. That really helped me learn more lisp.


Not totally about lisp, but carries a lot of principles used in lisp (recursive types, closures), and uses scheme : http://htdp.org/

Exercises includes sorting, search in binary trees etc etc

http://mitpress.mit.edu/sicp/ is almost the same but with more math/physic background (uses numerical oriented exercises very early). You'll have to write recursive subset, n-queens, matrix-composition functions.


You could have a look at the software below Hacker News. It's written in the Arc Lisp dialect (which itself is implemented on top of the Racket Lisp dialect).

Apart from Racket, Clojure is also a nice Lisp to get started with. It runs on the JVM.

I'd advice against learning Common Lisp as your first Lisp these days, even though Common Lisp is what On Lisp is about.


I'd advise exactly the opposite. While clojure and racket are excellent languages, they are very different from one another and CL. Telling someone to learn python or ruby if they ask about learning lua isn't a good advice, is it?


Oh, if your goal is to learn one of the Lisps, then don't spend any time on Common Lisp. It's old and ugly.

Similar, if someone asked about a modern scripting language, I'd send them away from Perl in favour of Python or Ruby.

If they goal was explicitly Perl (or Common Lisp) that would be a different thing, though.


>>Oh, if your goal is to learn one of the Lisps, then don't spend any time on Common Lisp. It's old and ugly.

why?

I don't find that at all, and a large reason for learning common lisp is as a learning exercise to become more intimately familiar with lambda calculus and s-expressions. The increased expressivity of clojure (for example) may hinder the very thought processes that make lisp a dialect worth learning. Of course they are both fine languages, but most people now advocate the learning of a LISP not for practicalities sake, but rather just to demonstrate real life things that those sets of languages do differently than nearly every other language, and to allow the programmer to take from those experiences and apply them to their language of choice.

From THAT standpoint, i'd advocate CL first, if not only for forcing the user of the language to experience the follies of certain practices, such as the namespace issues that Clojure tried to fix about CL.


> as a learning exercise to become more intimately familiar with lambda calculus and s-expressions

Any Scheme implementation will be better for this. Much less primitives, one namespace for everything, hygienic macros, immutablity by default/encouraged.

CL is exactly the opposite of that - it's complicated because it wants to be practical. And for the most part it is. It supports procedural, functional and OO programming, probably logic programming too with the right library. It's designed as a real system for solving real problems - and as such is not the best tool for learning about lambda calculus. Actually you could program imperatively in CL for the long time before ever feeling awkward.

Scheme, with it's mandatory TCO and encouraged immutability is much better for learning lambda calculus... and about Lisp in general.


Bullshit!

In many ways Common Lisp is still a much better language than Clojure or Racket. It isn't old, compared to pretty much anything from the 80's it has aged unbelievably well, and while it has some "stale" parts you can call ugly, I find many parts of clojure or racket much uglier. All in all, it is one of the best high level languages around.


Can you give some examples of those ways?


Any particular reason against Common Lisp?


Common Lisp is ugly (e.g. see https://en.wikipedia.org/wiki/Lisp-1_vs._Lisp-2) and not particularly functional. There are a few reasons you might want to learn it nonetheless, but they don't apply to someone new to Lisp.

(In case you ask: Common Lisp is a useful target, if you want to port programs from even older Lisps, like emacs' elisp.)


Oh I see by Ugly you mean practical. Lisp is about Metaprogramming not referential transparency. On the otherhand once you start diving in the source code of Clojure you quickly find yourself looking at javaclasses ime.

On the other hand CL has CLOS, an object system which puts others to shame. And reader macros to define your own syntax. See cl-annot to see the decorator syntax from python being ported to CL or how to give hashmaps Ruby syntax [1].

[1]: http://frank.kank.net/essays/hash.html


You can have CLOS in Racket, too. And Macros.

What's practical about a Lisp-2?


Have you given Practical Common Lisp[1] a try? Also SICP is a very good book, albeit a big deal time commitment. You can try Fukamachi's libraries or redline6561's cl-6502 as code bases to read and play around with.

[1]:http://www.gigamonkeys.com/book/


emacs


What percentage of the techniques in this book are also applicable to Clojure programming? I don't know a lot about how the two macro systems differ.


On Lisp is great for learning about macros, and what is possible.

That being said, what is idiomatic in CL is not necessarily idiomatic clojure. For example, pg tends to like anaphoric macros, (http://en.wikipedia.org/wiki/Anaphoric_macro), while Clojure tends to eschew them.


Have a look at http://thinkrelevance.com/blog/tags/on-lisp to see Stuart Halloway going through On Lisp in Clojure.


A fair amount of the examples involve mutation; a practice that Clojure discourages. A lot of the macros are very instructive, however.


Indeed, On Lisp has an excellent discussion of mutation and its use in programs written in a functional style. In discussing destructive functions, Graham describes the ways in which they can be used with reasonable safety - e.g. immediately mutating a list newly created by a mapping function.

The reasons one might do so in Clojure are likely to bear similarity to those provided by Graham - speed and memory - despite the vastly greater resources of today's computers versus those of 1991.


Very good point. Mutation is extremely useful when birthing a new data structure; this is a time when it's not yet visible to the rest of the system so the danger of sharing intermediate state does not occur. Clojure has a very elegant facility for dealing with this particular scenario:

http://clojure.org/transients


A lot of it. Clojure has a Common Lisp like macro system. There are some slight changes where clojure tried to make things a little simple, and the syntax is a lot diffrent.

I think there are people on the internet who have implmented most of the example in clojure.


No reader macros, though.


Also take a look on "Let Over Lambda" by Doug Hoyte about advanced macro techniques:

http://letoverlambda.com/

It's a pretty enjoyable book if you don't mind its preachiness. :-)


Great book, but not if you are just starting out with Lisp. For that, see PG's other book:

http://www.amazon.com/ANSI-Common-LISP-Paul-Graham/dp/013370...

Also make sure you check the errata for On Lisp, there are a few examples that you will really need the errata for.


Actually a much better place to start is Practical Common Lisp:

http://gigamonkeys.com/book/

Or if you are now to programming in general:

http://www.cs.cmu.edu/~dst/LispBook/


Cheers to PG for making this available.


Wowza, it has been 20 years since this book was published. Time goes by too quickly :-(


I thought 'oh, is this a new book'?

Then I looked at the date. It seems as though all 'classical' sources on lisp or functional paradigm are either two decades old, or just-out-yesteryear. What happened in between?


Did someone try (and possibly succeed) converting it to epub, by chance?



thanks! although the diagrams don't seem to work for me, unfortunately


Has anyone every tried to visualize lisp with a treemap? Maybe that would take care of the massive indentation problem that makes lisp so hard to read (for me).


It's 2013, and the top comment on a story about Lisp (on HN, a site written in Lisp) is complaining about syntax? Seriously, how much time did you actually spend learning/using Lisp before giving up because it had a "massive indentation problem"?


Surely expressing personal issues with Lisp's syntax is valid, especially if a possible solution is offered. If you see at least some of the larger codebases written in Lisp, it is natural to be overwhelmed by the level of indentation, which will cause the code to wrap under many editor setups.


"Lisp looks strange not so much because it has a strange syntax as because it has no syntax; you express programs directly in the parse trees that get built behind the scenes when other languages are parsed, and these trees are made of lists, which are Lisp data structures"


I've heard people complain about parentheses, but indentation isn't usually the issue. I challenge you: I looked at your personal website, and you seem to claim experience in Java and C. Show me a large codebase in Lisp and a large codebase in Java or C where the Lisp codebase is dramatically less readable than the Java or C codebase due to indentation.


You seem very upset. Calm down. I just had an idea that it might be fun to represent lisp code in a treemap. Or rather a zoomable treemap like this:

  http://bost.ocks.org/mike/treemap/


>it might be fun to represent lisp code in a treemap

Wow, yeah, that's a really interesting idea. Is there just one (good) way to map a codebase? Many?

It seems like a bad idea at first glance, since the map shows, at any level, more things with less context-per-thing when compared to straight text. It seems totally natural to use it for profiling. Refactoring would be a lot more grokable if it could all be visualized at once. But how would it work for scanning/editing & digging through documentation?



I very much like the mwe-color-box.el example. Is something like that possible for backquoting? The quoting-levels would be much easier to see with nested background shading. I'll have to try it at home.


Not upset, just tired of ignorant programmers who get off on complaining, and inventing solutions to imaginary problems (bad syntax? ooh, clever visualization!) rather than actually writing code. By the way, you didn't answer the question -- how much Lisp code have you written, "javajosh"?


Could you lay off the personal attacks?


What do you mean by indentation problem?

In practice, everyone just uses emacs to indent, no problem.


Oh, I was just thinking that it would be nice to visualize lisp in a more two-dimensional way, something like this:

http://bl.ocks.org/mbostock/4063582

I'm not really a lisp coder at all (a bit of clojure experience), but the relentless self-similarity of lisp languages is both a strength and a weakness, IMHO. But it's regular syntax would make it a good candidate for a treemap. Just sayin'.


Most clear clojure code doesn't look parenthetical at all really. Usually my code ends up looking like a few let declarations and then a single line of execution - and I'm only a beginner!

4clojure.com is a good resource to see how experienced clojure programmers can turn what would 50 lines of imperative code into a handful of keywords. The self-similarity is a non-issue. Saying it is a negative is like saying Mozart used "too many notes" in his music.


I'm not really a lisp coder at all (a bit of clojure experience)

In other words, you don't have a clue. Right, thanks for clearing that up.


And thanks to you for providing an example of the arrogance that turns most people away from the lisp community...


Indentation is optional in Lisp and solely exists to make code more readable by expressing the structure of the syntax tree. In contrast some languages, such as Python, require indentation due to attaching syntactic significance to the size of white space.

Because Lisp communities tend to eschew an imperative programming style, sequential operations tend to be composed of nested functions (and therefore indented) in lieu of passing values by mutable variables to blocks of sequential instructions.

Though arguments about programming styles are always going to remain unconvincing because the benefits of one over the other or vice versa are revealed when programmers are actually writing programs, it is objectively the case, that Lisp's indentation idiom can be used to express the sequence of execution for programs written using a functional style in a consistent way.


How large are these s-exp ? Lisp is unreadable when nested too much (I'm starring at an xml->s-exp dump and I don't even wanna try to read it), but usually lisp code and data tend to be factored into little combinators and separated specific functions. If you can, try to split them.


Here's a link to an example of that coping strategy from some bad Clojure I wrote last night [1]. I sometimes use an accumulation of lets for a similar effect, but only when describing simpler transformations.

The lets might look like so (warning- silly example):

    (defn n-squared-over-two-plus-three-as-str [n]
      (let [squared (* n n)
            halved  (/ squared 2.0)
            added   (+ halved 3)]
        (str added)))
     
    > (n-squared-over-two-plus-three-as-str 1)
    "3.5"
Sure, I could've just done this as a deeply-nested structure:

    > ((fn [n] (str (+ 3 (#(/ % 2.0) (* n n))))) 1)
    "3.5"
... but it's harder to read, debug, and reason about. You wouldn't do that in a non-functional language either! I suppose the tolerance for deep nesting is different for different programmers.

[1] https://github.com/dpritchett/cloball/blob/62300d31666ab1261...


That looks like a good place to use the arrow macro http://clojuredocs.org/clojure_core/clojure.core/-%3E


Yep, that's what the link in my original post was hoping to demonstrate. I find rolling up a few lets one after the other more natural in some other situations.


Although I'm second-guessing my let example now. Lets are great for assigning names to your building blocks, but I don't really like using them to obscure my processing flow as above. Perhaps this would be a better compromise:

    (defn example [n]
      (let [square #(* % %)
            halve  #(/ % 2.0)
            add3   #(+ % 3)]
        (-> n square halve add3 str)))
    
    > (example 1)
    "3.5"


If the function name or docstring is descriptive enough, I'd just go for

    (defn square-halve-add3-to-string [n] 
      (-> n 
        (#(* % %)) 
        (/ 2.0) 
        (+ 3) 
        str))


Thanks for the edition, the code wasn't as obvious. I believe Haskell `where` construct is to write code this way (beside the flipped order).


I think I should read it in emacs ...running on an ec2 instance


I look forward to reading this, however I object to the first reviewer comment: "The first book that really explains what Lisp is all about."

No, I think the first book to explain that was Godel, Escher, Bach.


OT

how is arc progressing?


The Clojure inventor (Rich Hickey) took most of the good ideas from arc and all the arc enthusiasts have pretty much changed horses long ago to Clojure.

I think arc still has some good points related to code brevity that noone else has fully duplicated yet.


No. Clojure's first public release was before Arc's first public release. Rich was also working on Clojure for two years before release.


I stand corrected.

(and besides, most of the best ideas of Clojure are certainly due to Rich's insights)


Lisp is a dangerous language that shouldn't be taught or used these days. There are plenty of superior programming techniques.

Mathematical programming - linear programming, finite domain constraint programming, constraint logic programming, logic programming, relational programming

Static functional programming - ml, haskell

A good modern introduction to programming is Concepts, Techniques, and Models of Computer Programming, by Peter Van Roy and Saif Haridi. It's far superior to the commonly mentioned lisp book Structure and Interpretation of Computer Programs.


>"Mathematical programming - linear programming, finite domain constraint programming"

Huh? I am not sure if you are trolling or if you copied and pasted that without further investigation, but comparing operation research algorithms (such as linear and finite domain constraint programming) with a programming language is at the very least... disingenuous.

> "Lisp is a dangerous language"

That's a very strong and weird statement, would you mind to explain? because to me is completely the opposite.


(putting aside the rudenss and cluelessness of your comment ...)

Operations research type algorithms can be thought of as forward chaining compared to prolog's backward chaining. Prolog implementations usually integrate them in because they are more efficient in many cases.

Lisp's dangerousness was why ML was invented by Robin Milner.


Sounds like you're an "anything that doesn't have static compile-time typing is dangerous" kind of guy.


Static typing is greatly beneficial, with HM it doesn't require a lot of annotation (people that use it find type annotations to be beneficial anyway). Languages that don't offer strong typing and GADTs are blub languages.


Common lisp is strongly typed, everything is of type t[1]

[1]: http://www.xach.com/naggum/articles/3284289178877169KL2065E@...


That's basically the consensus among professional academics on the matter. Also among well respected programmers in senior roles like Joshua Bloch and John Carmack.


The problem is static typing is applicable to only a subset of any particular programming task (in other words, even a Haskell or ML program that compiles is not necessarily guaranteed to be correct, insofar as it still may not produce the output desired by the programmer.)

Because of this, it introduces incidental complexity into a program, because it subdivides the programming task into a part that can be addressed via static typing and a part which is not purely an issue of typing.

Admittedly, in most cases this extra complexity is worth the cost (especially with the type of high performance code written by Bloch/Carmack) but at the end of the day the job of a modern computer is to make things easy for humans and not make things easy for the computer, so in the long run it's foolish to say humans need to "think like a computer" and use a static type system, instead of allowing humans to use a more natural, dynamic programming model that is more natural for us to use.


Sorry for the long rant ahead.

This is anecdotal, but I find that in general a static, strongly typed language actually helps me when I am writing the program. One quarter of the time, I find that the code I just wrote doesn't make sense and end up having a conversation with the compiler and REPL until it works.

One important place where I find types help me is error handling. I typically prefer to have Maybe/Option, Either, or similar types for normal errors, and to have exceptions reserved for when the program enters an unrecoverable state and needs to crash.

There are also cases where a dynamically typed language will error during runtime, and where a statically typed language would have instead errored during compile time. It is possible to encode invariants into types, for example to force all red-black trees to be balanced.

I haven't found a case yet where such an error wasn't due to flawed reasoning on my part, and depending on the language implementation it is possible to optionally defer type errors to runtime. The language I tend to use the most also has an escape hatch to allow dynamic types if you want them (implemented as a library).

Regarding incidental complexity, in a Hilney-Milner language, most code can be written without any type annotations whatsoever, although in my language of choice (Haskell), it is standard practice to include type annotations anyway to help readability.

Encoding invariants into the code does increase the complexity, and you'll see most library writers strike a balance between complexity and correctness. If you go for an even stronger type system than Haskell's (such as Coq's, Agda's or Idris's) then it is possible to encode all invariants using the types, although type inference doesn't work as well anymore.

Static, strong typing is there to help the programmer, and typing a dynamically-typed language is probably easier. Haskell's type system helps automatic unit testing libraries such as quickcheck and hunit work better. Computer hardware doesn't care about types, but rather where the bits and bytes are and what points to them. Static typing is there purely for the programmer's benefit, rather than the computer's.

I can't say that writing software in a language that has a weak type system is anywhere as enjoyable, and I find myself having to debug my code.


Just upvoted you for "Lisp is a dangerous language that shouldn't be taught or used these days.". You just made a huge number of people curious and willing to learn more about Lisp by saying that :)

Jokes aside, the Van Roy book is something to add to my "to read" list. Try posting it again in a comment that doesn't get downvoted to hell, maybe others will notice it too.


What kind of argument is this.

> constraint logic programming, logic programming

The book you comment on actually implments a logic programming system.

Both common lisp and clojure have implmentations of high performance logic systems.

> Static functional programming

Both Clojure and Common Lisp have type systems that can do most things ml and haskell can do. Its arguable that some of this (in the CL) are even more powerful then what haskell has.

Im not sure on what the other types of programming are but the do not at sound like things that a library can not do. Can you give a example of something that you can not do in clojure or commen lisp that is possible without in these other languages that you have not actually provieded.

> Concepts, Techniques, and Models of Computer Programming

It is a very good book. However 99.99% of programming today are not done with these modern concepts. It are actually langauges like clojure that push some of the technics like constraint logic programming more into the mainstream. Check out core.logic.


> Both Clojure and Common Lisp have type systems that can do most things ml and haskell can do.

I don't know about that, Haskell has quite a few very non-trivial extensions in it's type system that I have yet to see replicated outside of research languages. ( Rank-N Types, GADTs, Kind polymorphism, etc).



Both of those projects implement fairly simple type systems, they both even look simpler than even ML. Having at least outer rank-1 quantification is what I consider a "modern" typing system.


Im not a expoert. But I think at least Qi is as advanced as haskell or ml. The clojure one is relativly new and not jet full featured.


> Both Clojure and Common Lisp have type systems that can do most things ml and haskell can do.

Except they let you do MORE, which is often bad. For example, in Haskell STM you have a static guarantee that you don't have mutation or IO inside of a transaction. How are you going to guarantee that with Clojure STM?


That is absolutly possible with a static type system.


But that's not the main style of programming. If you teach someone lisp he'll hack around with functions and macros and not realize that his problem could be elegantly written in constraint programming.

Most of the time the majority of code (written by good programmers) is in constraints or relations, so why encase that in a functional language with parenthesis everywhere (I've programmed a fair bit in lisp and don't mind parenthesis at all, but it get's annoying after you're used to the elegance of prolog syntax).


People who have problems need to know about diffrent solutions. Just forcing everybody to use some langauge that supports some of these from the ground up want change that.

You want a language that is flexible to expand into new fields.

Also you have still not provided with these magical programming languages you are talking about. Is there any language you yourself would use?

As far as I know many concepts like contraind logical programming is not really ready for prime time, in most cases.


(you tamp down a little with your rudeness - why would multiple decades old tech be 'magical')

Prolog with clpfd and chr, Mozart/Oz, Sql, Clips. Libraries like gecode, java choco.

There aren't any new ways of programming that have been invented. There's basically the mathematical programming (constraint/logic/relational/linear), functional, procedural, oo, concurrent, stack. You're not going to come up with something new during a session of lisp hacking. You'll just reinvent the above multiple times.


I never said I invent something new. Lisp is a flexible languages that can incorparate new concepts better then other langauges. How nice is it to work if a logic programming system implmented within java compared with the same within clojure.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: