It is interesting to see people call an image a "cover" and a website a book even when the said cover actually never covers anything nor there is a book in there because a website.
PDFs and Word were originally designed (still meant to) for enterprise documents, not books. It works somewhat for books too but then PDF is not really "on web". People still have to download an artifact, save it somewhere or may be bother about other similar digital book formats the same way.
Why, viewing content on some god forsaken web site, page by page, through some proprietary plugin, with no ability to save other than taking a screenshot of the window.
+1. Kenny Tilton's blog is also great for that kind of thing.
"You know Lisp is the perfect language precisely because it lost its momentum (died) and lives on. Other languages need their Next Big Thing momentum."
I think the journey of a Lisper inevitably ends up with realizing that Common Lisp is not an acceptable Lisp. Then you start searching for an acceptable Lisp. You look at Tcl, at Prolog, Io, Rebol, and Icon; you learn Shen, try Scheme and Racket, appreciate Smalltalk and Self and Slate, wrap your head around Forth and Joy and Factor… and the whole time you write Common Lisp. Because there is no acceptable Lisp.
I fit this description minus a few of the PLs you listed, and added Extempore's xtlang, PicoLisp, and LFE (Lisp Flavored Erlang), but I am hacking in CL again this week!
.. and, finally, you realise that Common Lisp is an acceptable Lisp (because when you learn it well enough you start to understand how to tune it to make it acceptable for you personally ;)
Because lisp is an immensely flexible language/concept. Everyone has their own idea of what it should be/can be.
Common Lisp was exactly what it says "Common". Just some sort of common ground between all the various lisp dialects that existed, because everyone went their own way in extending the language. People end up coming back to it time and time again, because all the others don't quite provide that common ground for people to work on. Whilst there may be things missing, there's always something more missing in other lisps.
Very nice, I love seeing new Common Lisp books being written. (BTW, I also have a Common Lisp book on leanpub.com - a great company to work with if you are a writer).
It is especially good to see a complete caveman2 application developed in the book.
I am in the middle of reading Adam Tornhill's 'Lisp for the Web', and I just stumbled on this book.
Yes, Lisp seems to me to be getting more exposure, but that may be my 'Lisp-colored sunglasses' ;)
Is caveman2 a lot easier, or what other advantages does it have over piecing stuff together?
I have always liked and programmed in Lisp, but not for the Web, so I need to get up to speed with Web frontend/backend programming again for personal use, not a job.
OT: Mark, I am on your 3 edition of 'Loving Common Lisp', and I really appreciate the Networking chapter update, and your smooth writing style!
I'd love to read a chapter about why would someone choose Common Lisp instead of, say, Clojure (with all the JVM ecosystem behind it) or LFE (with the Erlang ecosystem behind it). What are the use cases in which CLISP stands out and offers a benefit?
LFE is not really Lisp, it is more like Erlang with parenthesis, so it is hardly comparable. Erlang offers a very specific, and useful, model of computation: if you need that, use Erlang or LTE.
In CL vs. Clojure, I suppose CL looks riskier to use than Clojure, w.r.t. all those JVM libraries (and the coolness factor). I think this is false, because you can access Java/Clojure/C libraries from CL with ABCL/CFFI, whereas you cannot access the Common Lisp ecosystem from Clojure easily.
But in the end, which programming language you use depends on many factors.
Let me try to rephrase my question: I know what I can use Clojure or LFE/Erlang for and what their strengths/weaknesses are. I don't really know anything about Common LISP other than it is a LISP, so I would love to hear some factors that led some people to prefer Common LISP to anything else out there (other than personal preference, which is always a factor).
Is it good at number crunching (which is not Clojure's and definitely not Erlang's forte)? Is it good at low level systems programming? Is it good for computers with low memory resources? Etc. In your words, I am looking for the context.
That's a very complicated question. Common Lisp has many implementations, with different strengths and weaknesses. For example SBCL and CCL are good general purpose implementations. They're fairly performant, especially SBCL, properly optimized code can compete for C-like performance. ABCL gives you Java integration, not as nice as clojures for example, but perfectly usable IMO. ECL has a fairly small footprint and is good for embedding, it can also compile down to C I think. Clasp is a newcomer to the Common lisp world, it compiles to LLVM and provides C++ integration. It's creator uses it for chemistry and nanotech simulations, so he has many C++ libraries he wants to use with Lisp, so he wrote an implementation. There are also commercial offerings(LispWorks, Allegro CL) that come with additional support and their own graphical IDEs.
When I switched from Clojure to Common lisp a few years ago, mostly I really like using multi-paradigm languages. Clojure code wants to be functional. Common lisp code has no such preference, and with the excellent OOP support it's a pleasure to use. Basically it's generic function based, rather than class based, which means that your methods are owned by your generic functions, rather than your classes. This gives you much more flexibility and power than "standard" OOP. I can write software in the style that best supports the problem I'm trying to solve. Other great features include the condition system, basically exceptions on steroids. Very few languages have something similar.
Another one of my favorite features is that the standard has been stable for decades. The core language hasn't changed since 1994, so the ecosystem continues to evolve constantly, but the core stays stable and it isn't unheard of for code written in the 80's to run with little or no modifications. I like the idea that my code might have that kind of longevity.
I could go on, but you get the general idea of having an extremely mature and stable language with many implementations that all try to solve different problems, with sufficient portability that fragmentation doesn't tend to happen(like in the scheme world for example). And personal taste of course, It's just a pleasure to use for some reason. I don't know why, but I'm just having way too much fun playing with it and trying to explain it to others.
Speaking as a longtime (nearly 30 years) Common Lisp programmer, I agree with what you've said here, but I would add one thing more. Common Lisp is one of a small number of languages that represent an approach to programming that is so out of fashion nowadays that it seems to be in danger of becoming extinct. The approach is one that treats programming as an exercise in interactively shaping a living thing in real time, rather than building an artifact from a blueprint. Programming-as-construction is the mainstream; Common Lisp and a small handful of other languages represent a different approach that we might characterize as programming-as-teaching.
Common Lisp and some older Lisps, along with Smalltalk and FORTH, treat programming as an ongoing interaction in which your main program is already running before you ever write any code. It's running, but it doesn't yet know how to be your application. You have to teach it. This you do incrementally, one small change at a time, gradually guiding and shaping the running program until, eventually, it becomes your application. It continues to run throughout the entire process; it just changes its behavior as you teach it new capabilities.
Common Lisp (and a few other languages) are designed with this approach to programming squarely in mind. Consider, for example, the standard Common Lisp functions CHANGE-CLASS and UPDATE-INSTANCE-FOR-REDEFINED-CLASS. In most languages these functions make very little sense. Once you understand how Common Lisp is designed to be used, it's obvious that they are necessary. They're necessary because obviously you're going to redefine classes as you progress, and obviously you don't want your running application to fail simply because you've redefined its types out from under it. So the language is designed to support that way of working. It provides facilities for gracefully handling the situation that arises when you redefine types out from under your code, and it gives you ways to ensure that the program keeps running as you make those changes.
This way of working is now sufficiently out of fashion that even new Lisps don't necessarily support it. Clojure isn't great in this area. Neither are some Schemes. Common Lisp and Smalltalk are the strongest remaining bastions of programming-as-teaching, as far as I can see.
Almost everywhere else in programming today, tools and languages uncritically assume the programming-as-construction model. Maybe that's okay. There's certainly good work being done in that mode. Programming-as-teaching is my native idiom, though. I'd guess that I'm somewhere around ten times more productive when working that way. That doesn't mean it's a better way, of course; it just means it's better for me. Nevertheless, I can't help but mourn when I think that maybe programming-as-teaching is disappearing from the world.
I wrote some similar thoughts here: http://lisperator.net/blog/why-lisp/ (in the "organic growth" section). Sadly I don't get to do much Lisp these days, but it remains my favorite programming language (CL, more exactly).
And yeah, it's sad that "programming-as-teaching" fades out of fashion. Now that I know what it's like, working in an environment that doesn't support it always leaves a bitter taste. Let's just hope more people try it, because it's addictive.
Interesting comment. I suppose it is along roughly the same lines as Paul Graham's essay about top down vs. bottom up programming (?) Read it a while ago, not handy on mobile right now.
What is it about CL / Lisp that makes people think it more suitable than other languages for using in such a style? It cannot be only the REPL, since others have them too. Or are CL REPLs more powerful? I've only explored one a bit, Franz, IIRC, some time ago. Edit: Okay, you gave two examples above, CHANGE-CLASS and another. Any other good ones?
Also can you elaborate with some examples on how the condition system is more powerful than other error handling approaches? I saw that mentioned here on HN recently, IIRC.
You're asking for a list of functions that takes interactive programming into account, and I'll give you one, but first I want to make the point that it isn't all about lists of functions or features. It's about a language and system being designed from the start with interactive programming in mind. If we focus on language features in the sense of grammar, syntax, and functions, we're likely to miss the point. You don't for example, turn a programming system based on programming-as-construction into one based on programming-as-teaching by adding functions. You need to design the system from that start with the assumption that you will be building your application interactively while it runs.
Common Lisp isn't the only language designed that way. Older Lisps were, too. So was Smalltalk. But that view of programming is now so far out of the mainstream that even new Lisp dialects don't reflect it.
But you asked for examples of other Common Lisp functions that take interactive programming into account. Instead I'll offer a mixed list of functions, special forms and design features. Don't assume it's exhaustive; there are too many to reasonably expect that I can list them all.
- COMPILE doesn't mean compile a file; it means compile an arbitrarily chosen function that might not even be in a file anywhere
- EVAL-WHEN enables you to control whether forms are evaluated at compile time, load time, or toplevel-evaluation time, because you might have program text that is intended to be evaluated at any combination of those times. Again, its behavior is independent of whether the affected forms appear in files (though obviously that's the most common place for it to appear).
- namespaces are represented by packages, which are runtime objects that you can interactive inspect and modify. The namespaces represented by packages have a concept of public and private names, but you can't really hide private names, because if you could it would be impossible to change them at runtime. Common Lisp is allergic to features that are impossible to change at runtime. It has to be in order to support interactive programming.
- CLOS has a whole protocol for changing the definitions of classes and functions while a program runs. CHANGE-CLASS and UPDATE-INSTANCE-FOR-REDEFINED-CLASS are members of that protocol. SO are REINITIALIZE-INSTANCE and SHARED-INITIALIZE and UPDATE-INSTANCE-FOR-DIFFERENT-CLASS and ADD-METHOD and REMOVE-METHOD and DEFINE-METHOD-COMBINATION.
- introspection is pervasive in Common Lisp. It has to be. It's an interactive system in which you build programs while they are running. You have to be able to examine every part of the running program as it runs. So it has scads of functions for examining the structure and value of everything in the running program--and not just examining it, but changing it. There's BOUNDP and SLOT-BOUND-P and SLOT-EXISTS-P and FMAKUNBOUND and FIND-METHOD and TYPE-OF and CLASS-OF and SYMBOL-FUNCTION and so on and so forth. DISASSEMBLE, which decompiles compiled code so that you can interactively examine what the compiler produces, is part of the language standard.
- the assumption of interaction is also pervasive. TRACE and INSPET are part of the standard. So is BREAK--a function that causes the program to enter an interactive breakloop in which you can examine the dynamic state of the suspended computation, inspect and change the values of local variables, redefine functions and methods, and resume the interrupted computation when you think you've made the changes you wanted.
- you asked about the condition system. You could say that it's Common Lisp's implementation of exceptions, and that would be true as far as it goes, but it would also ignore important aspects of its design. Yes, you can raise a condition, just as you can raise an exception. However, the normal behavior of an unhandled condition is to drop you into an interactive breakloop from which you can modify the suspended computation and all of its accessible state.Conditions provide restarts -- a mechanism for offering a set of options for resuming control at a place of your choosing, or of selecting how to resume execution under program control. Conditions are also more generally useful as ways to manage nonlocal transfers of control.
- it's not explicitly part of the standard, presumably because it was such a fundamental assumption of Lisp programmers when Common Lisp was being defined, but nearly all Common Lisp implementations support saving a serialized form of the dynamic run-state of the system that can later be reloaded to reproduce that dynamic run-state exactly. It means that you can save the exact environment that you're working in right now, complete with interactive state like breakloops and so forth, and resume it later. You can, for example, discover a bogu, dig into what you can find out by inspecting the dynamic state of a breakloop triggered by the bug, and save off that whole dynamic state, then reload it on another machine so that you can show it to a colleague in all its glory.
There's more, of course. It's hard to reduce a design that is all of a piece to a list of discreet features.
I barely disagree. In that that 'way' is not dying, it's just that other languages spread the fundamental layer away just enough so that what people have for free in lisp/st/forth/ml takes a whole new organ (IDE) to reinvent flatly, but it's constantly reinvented.
These systems are so coherent they solve themselves in and out, but if you mess a few things it's now compounded interests fueling your technological debt.
Thank you for the detailed answer, I think you could expand on it and include it in your book as an introductory chapter, or even as a blurb (especially the second paragraph).
I've been dabbling in Clojure for about 4 years now, off and on, and it works very well for small-ish projects. The integration, as others have mentioned, with the JVM is superbly seamless (it leaves Scala in the dust in this respect) and this opens up most of the Java ecosystem. Also, the STM model of concurrency management is super cool. So far so great!
But every time I try to build something more substantial than a Project Euler submission, I end up with something blowing up in my face. Recently, I was a bit annoyed to discover that the set of keys of a map doesn't have the same semantics (including sequential access) as other sets, and on another occasion I was debugging something and the contents of some variables that displayed valid data in "print" statements showed as "null" in my debugger. None of this is show stopping but when I'm just playing around I have a low frustration threshold. My impression is that Clojure is a good Lisp with many fantastic new features but not all of the batteries are included (yet).
Meanwhile, I have zero experience with CL but I keep hearing that it's highly mature, rock solid, the choice of most professional Lispers, and the platform of choice for Peter Norvig's book on AI programming. In my imagination, that means everything works exactly as expected and there is a wealth of useful libraries - which would be sweet, from where I stand.
> Meanwhile, I have zero experience with CL but I keep hearing that it's highly mature, rock solid, the choice of most professional Lispers, and the platform of choice for Peter Norvig's book on AI programming.
It is definitely highly mature, rock solid and the choice of most professional Lispers. It anticipates many problems one doesn't even know one will have, and solves them.
In the negative column, it's a bit crufty for historical reasons (e.g. (aref ARRAY 1 2) vice (gethash KEY HASH-TABLE)) and made some choices which in retrospect were foolish (upcasing symbols being a huge one). The former doesn't really matter, and the latter can be solved with a simple (setf print-case :downcase).
For a long time, there were few good resources for learning Lisp. Fortunately, that's changed. Peter Seibel's Practical Common Lisp (http://www.gigamonkeys.com/book/) is a must-read: it explains a little of why Lisp is so relevant for modern development. Edi Weitz's new book Common Lisp Recipes (http://weitz.de/cl-recipes/) is worth its weight in gold.
Some advice for Clojure that applies to all lisps: write code bottom up in small pieces making heavy use of the repl for development. Really understand what your small piece of code does, clean it up, put it in a separate small file, and once you have confidence in it think of it as a library that just works.
Clojure seems to be a natural choice for microservices, but I never used it for anything on a larger scale. Good point, if it is something Common LISP gets right it is definitely worth a look!
It is a way to customize the Lisp reader by associating a custom function to an entry in the readtable.
So you can say that character '[' will be tied to a custom reader function, which reads from a stream and produces a Lisp object, at readtime.
The standard sharpsign[1] character is used to read different kind of structures: complex numbers #C(0 1), structs #S(tree :left nil :right nil), and so on.
In particular, you can have sharpsign-dot, which reads a lisp form, evaluates it at read time; the result is the form being read.
Also, you can use #1= and #1# to affect parts of your code to reader variables and reuse them in other places.
If the PRINT-CIRCLE (there are earmuffs around it, but I can't format it here) variable is set to true, here is a quine:
I found them useful when I looked at clojure and saw their shorthand syntax for lambdas where #(* % %) is a lambda that takes one argument and squares it. You can get some tidy things that are similar with regular macros but to make it actually equivalent your need to hook into the reader..and that's what reader macros are for. So within the hour I had something that let me write λ(* _ _)
The best bit though, is that this isn't some nasty hack. This is supported by the spec, so I can package this up and let other people us it just like any other functionality we care to ship around.
That's my favorite thing really, being able to treat approaches to writing code in the same way we treat the functionality we make using code.
> That's my favorite thing really, being able to treat approaches to writing code in the same way we treat the functionality we make using code.
You mean ... programming the programming language?
> I can package this up ...
Not really; it will clash with someone else's use of λ. Read macros do not "package" particularly well. (Racketlang has a good solution for this: you put a #lang whatever directive at the top of a file and then the rest of the file uses the special syntax associated with that lang name).
It's very easy to overestimate the usefulness of read syntax.
This feature of Common Lisp is used far less than newcomers might imagine, though there are some famous examples of it. If you randomly sample Lisp code, you are much more likely to find a macro than a read macro.
> So within the hour I had something that let me write λ( _ _)*
Do you plan to type "lambda" with args enough times to eventually that hour of your life back? Ha.
Useful syntax for writing anonymous functions can be had, without relying on read macros at all.
Note how if we take λ(* _ _) and then just move the parenthesis over the Greek symbol, we get (λ * _ _). This saves almost the same amount of typing. If your Lisp recognizes λ as a symbol token, then all you have to do now is write a regular macro. Basically, just get the cl-op package and then set up λ as an alias for op: (defmacro λ (&rest args) `(op ,@args)).
In TXR Lisp, I implemented a more powerful op which has numbered arguments, and an escape syntax for inner ops to refer to outer ops.
Y combinator with TXR Lisp op:
;; The Y combinator:
(defun y (f)
[(op @1 @1)
(op f (op [@@1 @@1]))])
;; The Y-combinator-based factorial:
(defun fac (f)
(do if (zerop @1)
1
(* @1 [f (- @1 1)])))
;; Test:
(format t "~s\n" [[y fac] 4])
Combinators return us to your topic of writing a function which squares its argument. Instead of a condensed lambda notation like (op * _ _) for doing this with syntax, we can have a combinator, like (dup #'). This is nicer in a
Lisp-1 language where we can just do (dup ). Dup takes its argument, which is a two-argument function, and returns a one-argument function which calls that function with two copies of the argument:
Hehe, what are curiously peeved sounding post. I was seeking to keep it short but as I've been unclear let's drill into some stuff.
> Not really; it will clash
Nope, named-readtables fixed the clashing reader-macro issue a good while back.
> You mean ... programming the programming language?
Yes, I do. I said it the way I did out of choice, as the 'programmable programming language' phrase has been bandied around so often that, for some, it has lost impact. Maybe that phrase doesn't inspire the same ahah moment in some as it does for other. Saying the same thing in a different way can be enough for someone to pick up something new. (It feels weird writing this down as it's kind of self evident)
> Do you plan to type "lambda" with args enough times to eventually that hour of your life back
I that hour was spent learning about reader macros, learning something new is of value to me, so there is no 'lost hour' to reclaim. Don't you just code for fun some days, without worrying if it is somehow ultimately useful?
> Note how if we take λ(* _ _) and then just move the parenthesis over the Greek symbol, we get (λ * _ _). This saves almost the same amount of typing
Ah ok, maybe this is where we parted mental ways. I'm not interesting in saving typing, that wasn't the goal. The point was to remove a little visual obstruction from what I was doing. For example (and yes it is a trivial example) (lambda (x) (* x x)) is 20 characters long, of which 8 are of interest (* x x) I liked that the shorthand reduced a little visual noise.
Yes there are many ways to skin this particular cat, but I liked the shorthand. I also like not having the lambda in the first position as it maintained a property of lisp code I generally enjoy, that I can run my eyes over the lefthand side of the forms and quickly get a rough feel of what is going on. Again, this is just a personal feeling, but some days I write code just for me, for fun, so why not make things pleasant for myself.
It's rather odd to get into a discussion about this, when the whole point is that we don't have to pick one golden way and enshrine it in the language. It's just a library, use it or don't, it doesn't matter.
Packaging means "better than just loading something and having it pee in the global readtable". It doesn't solve the problem that read macros compete for the same characters and clash.
Documentation for merge-readtables-into:
"If a macro character appears in more than one of the readtables, i.e. if a conflict is discovered during the merge, an error of type READER-MACRO-CONFLICT is signaled."
Exactly, and this is awesome. If there is a conflict, I get an error and can decide what to do.
At their core, reader-macros are abbreviations. Obviously there are a finite number of abbreviations one can use, so any time you want to add concise syntax from more than one source, there is a chance of conflict.
I can see how this would have value if your idea of Lisp programming is to bring half a dozen new read syntaxes, and use them all in the same source file. Plus keep up with upstream enhancements in all of them, so you have to guard against new clashes popping up.
Do you mean reader macros? Those are used to implement new kinds of syntax within Common Lisp's parser, the READ function. Even standard parts of Common Lisp syntax are defined this way, such as quote (') or backquote (`).
A diagram helps when you start getting into read/compile time discussions, see slides 13 through 17 of http://www.slideshare.net/adorepump/clojure-an-introduction-... to see how the evaluation model for Clojure (which is similar enough to Lisp) differs from most other languages. One of the many differences between Clojure and Lisp though is that Lisp supports user-defined reader macro definitions, so you can add things like the nice persistent map literal syntax of Clojure to Lisp if you really want to.
Read macros are great because you can use them to turn the Common Lisp parser into a parser for any language you want (no restrictions on the grammar), so you essentially get compilers for free. I wrote a one-pass C preprocessor and parser in just under a thousand lines of code: https://github.com/vsedach/Vacietis/blob/master/compiler/rea...
This is pretty much wrong. C macros have a simple substitution language. But all Common Lisp macros can run arbitrary common lisp functions to compute the macro expansion. That means, you are not limited to pattern matching, but can write a program - in the same programming language you are using for your application - to compute the expansion. Of course there are simple cases which are just syntax rewrites, but there are also complex cases, like the loop macro. The loop macro allows you to specify looping constructs in an almost natural language.
Agreed. I think that the line numbers and machine-inserted hard breaks make it difficult to read. For the first issue, maybe the line numbers could be smaller and/or greyer, and offset more? For the second, I think human editing of the lines is probably required.
Assuming Lisp is correctly indented, it's quite easy to read. You can just ignore the parentheses for the most part, and concentrate on the layout and the built in macros - defun, let, do/loop, cond/if/when/unless/case, lambda, ...
If it's written in a functional style it becomes even easier: you don't have to hold any state in your head.
Is it full stack Lisp in the sense that Lisp is running on both the server and in the client (browser/mobile)?
If it is the latter, that would be quite compelling. Clojure/Clojurescript is quite compelling from a developer point of view, since you don't have to switch mental context to develop your server and client code (as you would have to do if you develop Go on the server and JS in the browser).
In Potato we did exactly this. The server side is written in Common Lisp and the client side is implemented in Clojurescript.
While I have to say that it has worked pretty well, Lisp and Clojurescript are different enough that it really isn't much of a benefit to use both together. Even the S-expression format of the two languages are so different that we need to use JSON when sending messages from the server to the client (and vice versa, of course).
Please feel free to look at the code if you want to learn more about how it's done. I'll be happy to answer any questions: https://github.com/cicakhq/potato
Interesting book. Your introduction covering toolsets, choice of lisp etc was enough that I bought a copy!
I've been seeing (common) lisp everywhere lately, from talks on clasp to the recent(ish) work on (really) fast http parser for lisp etc. It really feels like cl, with the help of a few, but very much needed tools and libraries (like quicklisp) and mature implementations that compile down to machine code could have a bit of a renaissance.
I think a lot of people simultaneously realized that one didn't have to code old style lisp, just because the cl spec has been written. Like the project to reimplement cl in cl. There's no need to rely on old broken crutches, just take the good parts of cl, combine with 40 years of experience from system and language engineering, and reduce back down to cl. And suddenly you have something very elegant and efficient and powerful.
Ok, I admit, I still want "sweet expressions"[1] -- but I think just a simple thing like acknowledging that global variables with * earmuffs * , are still global variables - and generally a bad idea -- and that cl is much more than some small "functional" language goes a long way. I remember I came to lisp rather late, and was confused by how un-functional a lot of the code was. There was just a lot of messy examples with detached functions for manipulating ad-hoc data structures that didn't seem properly motivated at all. And it all seemed terribly brittle.
But if one ignores all of that (and a lot of the classic lisp books), what emerges is that the "good" parts of cl are really great, and there's really no reason not to use those parts. And the warts in moderation (as with any other language).
I'd love to see more books like this for less common languages (Haskell particularly), as I've found there's generally plenty of resources for learning the basic commands and code structure but there's little information for going from those first steps to creating an actual useful application.
With this book, we want to show you how to use functional programming and Haskell to solve realistic problems. This is a hands-on book: every chapter contains dozens of code samples, and many contain complete applications.
Real World Haskell is somewhat out of date. I'm not sure what to go with instead, but I've heard good things about Haskell Programming from First Principles (which has already been mentioned in this thread).
or you realize Perl, Ruby, Python give you everything good from Lisp anyway with an environment and packages that can talk to the real world, and that macros and metaprogramming are massively overrated and a danger in almost any hands that touch those features.
Ask me how long I spent banging my head against the wall trying to get OpenGL working in CMUCL with Alien. Jesus jumping-jack Christ. I can only hope things have improved in the past 12 years or so. But I highly doubt it.
We gave you the benefit of the doubt originally, despite the nasty embers burning in your comment, but then you went full flamewar downthread. We don't want that on HN, so please don't do it again.
Personal attacks are not allowed on Hacker News. We ban accounts that do this, so please don't do it again.
Usenet-style programming flamewars are particularly not wanted here. The founding idea of HN is to try to learn from the community-destroying experiences of the past, and not repeat them, or at least stave them off.
I did python professional y for a few years and I missed lisp every day. There is much work to be done, but lisp has improved tremendously in the last decade.
I don't think that's true. For instance, Python has traditionally been really bad for concurrency but Lisp can be best-in-class (see Clojure's STM). Python is good for single-threaded concurrency now with coroutines and libraries like asyncio, but it still can't compete with something like STM for multithreaded concurrency.
Scott is right: you can't comment like this here. Personal attacks and name-calling will get your account banned from HN, so please don't do it again.
You obviously know quite a bit. Please make your posts valuable to others by being civil and substantive, especially when others know less than you do. Then we all learn.
I like Common Lisp too, and agree with most of the substantive parts of what you've said, but that is less important than preserving this community. We detached this subthread from https://news.ycombinator.com/item?id=11826768 and marked it off-topic.
Why not remove that obviously trolling comment?! It has no value and the list of stuff does not need to be preserved. The Lisp community has already good pointers to resources.
This is another troll who calls others 'retarded' to stir up controversy and have fun with it.
We don't do that as moderators, short of banning the account. I'm not banning that account because its other comments are fine, and it's better to assume that people are willing to improve.
HN's mechanism for dealing with individually bad comments is user flags, and the two bad comments in question have indeed been flagkilled.
And as a big fan of Common Lisp, I have to say, this is the kind of evangelism we don't need. (Well, the list of libraries is nice, but the bigotry we can definitely do without. It is possible to express strong opinions without calling others ignorant, or worse.)
Work in progress is a fine thing to have on Hacker News as long as there's a way for users to check the work out. In this case there's a sample chapter, which more than clears that bar. In fact we've added "Show HN" to the title, because this is the kind of thing that Show HN is for: https://news.ycombinator.com/showhn.html