On Lisp
paulgraham.com
paulgraham.com
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'.
There are of course still tons of examples in the book that are very useful for clojure as well.
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.
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.
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.
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.
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.
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.
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.
(In case you ask: Common Lisp is a useful target, if you want to port programs from even older Lisps, like emacs' elisp.)
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].
What's practical about a Lisp-2?
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.
I think there are people on the internet who have implmented most of the example in clojure.
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.
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.
It's a pretty enjoyable book if you don't mind its preachiness. :-)
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.
Or if you are now to programming in general:
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?
http://bost.ocks.org/mike/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?
In practice, everyone just uses emacs to indent, no problem.
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'.
In other words, you don't have a clue. Right, thanks for clearing that up.
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.
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...
(defn example [n]
(let [square #(* % %)
halve #(/ % 2.0)
add3 #(+ % 3)]
(-> n square halve add3 str)))
> (example 1)
"3.5" (defn square-halve-add3-to-string [n]
(-> n
(#(* % %))
(/ 2.0)
(+ 3)
str))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.
No, I think the first book to explain that was Godel, Escher, Bach.
how is arc progressing?
I think arc still has some good points related to code brevity that noone else has fully duplicated yet.
(and besides, most of the best ideas of Clojure are certainly due to Rich's insights)
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.
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.
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.
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.
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.
[1]: http://www.xach.com/naggum/articles/3284289178877169KL2065E@...
> 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.
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).
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.
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 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).
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?
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.