Hacker News new | past | comments | ask | show | jobs | submit login
The Common Lisp Cookbook (lispcookbook.github.io)
264 points by macco on June 29, 2017 | hide | past | favorite | 199 comments



For a more modern and up to date guide, Edi Weitz's "Common Lisp Recipes" is a great reference: https://www.amazon.com/Common-Lisp-Recipes-Problem-Solution-...

The more I use Common Lisp, the more disappointed I am that it hasn't become more popular. It really is higher level than Python, Ruby, or Perl but with nearly the performance of C and C++.


> The more I use Common Lisp, the more disappointed I am that it hasn't become more popular. It really is higher level than Python, Ruby, or Perl but with nearly the performance of C and C++.

It's nice to hear that I'm the only one. I, not too long ago, picked up Common Lisp and after a bit of "getting it", it suddenly became one of my favourite languages. And by "getting it", I'm not talking about the difficulties people normally refer to when talking about learning lisps, it was learning how to develop with it.

There's a huge productivity boost in just the fact that your development process is as follows: boot REPL, run your program and then modify your program (while running). If anything is to break, or hasn't been implemented, the debugger just stops your program, let's you inspect your program state freely and you can update the code that failed and have another go.

I'm normally very much to the strong, static typing side of languages. But to me, the Common Lisp experience is dynamic done right, it gives all the tools to handle the problems that arise from a dynamically typed language (even though Common Lisp is actually partially strongly typed) with is a great boon to productivity.


You're definitely not the only one. I'm in the camp too. The language isn't perfect, but it's so much better than the popular dynamic ones that it's a shame it's still relatively obscure. And the development model of Common Lisp is what I strongly miss everywhere else.


By "development model" do you mean how the Common Lisp project is developed or how developers develop with Common Lisp?


Aren't those essentially the same? But if there's a difference, then I think mostly of the latter - of development experience when working on Common Lisp code.


Yes I suppose they are. :) I meant to ask if it was something about the way the project made decisions or picked direction. But it's clear now what you meant.


Honest question, how is this different from what you can do in e.g. python? The python interpreter supports reloading of modules and evaluation of expressions, is there functionality that CL has above and beyond this that makes it more powerful?


Can you recompile a function on a running program and have it pick up the new definition? Can you change a class at runtime, and then adjust already existing objects to match the new definition in a controlled way, without data loss? Can you forget to define a function, or initialize a variable, only to be allowed by the debugger to provide a new definition / value on the fly and continue execution?

Those are some examples off the top of my head, but the general point is that unlike mainstream languages, Lisp languages evolved into environment for interactive construction and modification on a continuously running program, which is a bit different way of working than the regular write-compile-run cycle.


Some, most?, of the things you mention are available in other languages but they generally do not come together so well and they feel clunky.

I think it's not so much whether the features exist in other languages since we all know they can be added but the whole being more than the sum of its parts.


I find Clojure to be practical today, and it supports most of those. I believe it lags a little behind CLs development features, but since it tends to force immutable and referentially transparent code, I find that helps too. Since swapping things out is trivial in those cases.


With Javascript as an example, it can indeed be used in this fashion, as I expect also Python. But the key difference is the community of CL is this is a standard way of working, and the tools are built around this.


I'm not much of a Pythonist, but I have been doing CL since before ANSI CL.

To me the thing that CL has that has yet to be matched by any other languages, REPL-based or not, static or not, is the amazing condition system and dynamic restarts for handling problems. For building and debugging systems this is an utterly amazing thing. It also allows composing exceptional conditions in a reasonable way.

Every other language I use (e.g., Clojure these days a lot) I constantly miss this amazing stack-walking ability and conditional restarts.

Every time I boot my old Symbolics computers and something happens and I get a GUI restart, I'm just amazed at what we have lost through two decades of non-CL living.


The sound of it makes me think or Erlang's OTP system. I've just been really digging into it lately. Though the stack walking seems different, the entire OTP system is wonderful in letting you deal with errors by composing restartable processes and supervisors.

Heck, it's easier to handle recovering from an error by just restarting a process and letting it follow the normal unit logic.

Do have any knowledge of how CL and Erlang/Elixir/OTP are similar or vary on error handling?


Common Lisp's restarts are not restartable processes in the Erlang sense. They are part of a facility for suspending execution of a thread of control when it encounters an error, modifying the dynamic state of the process during the suspension, and resuming execution afterward.

When an error occurs in a Common Lisp program the runtime signals a __condition__. A condition is an instance of a class that represents unexpected or otherwise exceptional situations that can arise during execution. When a condition is signaled, control searches for a registered handler that matches the type of the signaled condition. If one is found then control passes to the matching handler. If none is found then control passes to the default handler, which by default starts an interactive repl called a breakloop__.

The breakloop has access to the full dynamic state of the suspended computation, including all local and dynamic variables and all pending functions on the stack. From the breakloop a programmer can alter variable values and redefine functions and classes. For example, if you conclude that the error happened because of an incorrect function definition, you can redefine the function and tell the breakloop to resume execution as if the new definition had been called instead of the original one.

A __restart__ in this context is an option for continuing execution. Common Lisp's condition system offers the programmer the ability to choose from among available restarts in a breakloop, or to write a handler that will make the choice automatically when a condition is signaled, and it also offers the ability to define custom restarts.


I've been wanting to experiment with this. There are some libraries in Clojure that try, but it seems that the real value is the interactivity of it while developing? I haven't found the exception handlers to really add much value over normal exception handling at least in Clojure.


As an interesting example, conditions and restarts can be used to implement an interactive system that is composable with other programs. For instance, you can implement a simple y/n question handler as follows:

    (defun fn1 ()
      (if (yes-or-no-p) (print :yes) (print :no)))
    
    (defun fn2 ()
      (fn1))
[yes-or-no-p](http://clhs.lisp.se/Body/f_y_or_n.htm) is an interactive function that reads from the stdin. However, you cannot programatically answer "yes" to fn1, as other functions in the call stack (fn2) has no way to know that fn1 halts because it waits for the input from the stdin. Instead, a condition system allows this:

    (defun fn1 ()
      (restart-case (error "yes or no?")
        (yes () (print :yes))
        (no  () (print :no))))
    
    (defun fn2-interactive ()
      (fn1))

    (defun fn2-automated ()
      (hander-bind ((error (lambda (c) (invoke-restart 'yes)))) ;; handler
         (fn1)))
When you call fn2-automated, an error is signaled, handled by the handler, which invokes a restart 'yes, then :yes is printed. Interactivity is still maintained by fn2-interactive.


I'm not sure I get it. Where is the user io in the second example? I'm not sure I understand how fn2-interactive still works.


It enters the debugger due to the error. In the debugger menu you see additional entries YES and NO.

    yes or no?
       [Condition of type SIMPLE-ERROR]
    
    Restarts:
     0: [YES] YES
     1: [NO] NO
     2: [RETRY] Retry SLIME REPL evaluation request.
     3: [*ABORT] Return to SLIME's top level.
     4: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING                 {1003167FA3}>)
See? All debugger menus are actually implemented as the restarts, set up at various call stacks. In this implementation of the debugger the way to select YES is to enter 0 or click it on the emacs buffer. In another debugger, it could be entering ":r 0" to the stdin. You can also implement your debugger function, which `read-line` the input stream and only recognizes a specific string, y or n, and set your function to `debugger-hook` to use it (I forgot to mention this in the above example).


Ok right. That's why I feel in Clojure it doesn't provide as much, because it doesn't hook into a debugger.

Having said that, practically, you wouldn't release a tool which has users interact at the debugger, at least for any level of more serious commercial work no?


CL's debugger is just a function, and the key is not to be distracted by the name "debugger".

As said, you can have a custom "debugger" which does not look like a debugger, but a general purpose user interface in the text terminal. For example, being "custom", it can also hide the restarts which are only meaningful for true debugging (like restart 2,3,4). (see detail [1])

It is possible for a debugger to even invoke a GUI (imagine a debugger hook which pops up a window with an OK button) or a web UI (sends a Websocket message to the client, wait for the user to click something on the browser, receive a reply and invoke the restart). In fact, SLIME is written this way: it overloads the debugger hook, and it sends and receives messages to the Emacs TCP client (thus you can click on the menu on an Emacs buffer to restart -- this action is notified back to the underlying lisp process via TCP, which then calls invoke-restart).

[1] You can obtain the list of restarts with COMPUTE-RESTARTS and filter unassociated restarts (similar to a method dispatch but is temporary / has dynamic extent -- see http://clhs.lisp.se/Body/09_adbd.htm).


I see, that's cool. Thanks for the info. So it can be used for certain form of event modeling, like a built in event system. That does sound cool.


After fooling around with porting some Common Lisp code to Python as an exercise in reinforcing my understanding of the underlying algorithms and learning Python, I would describe the difference as the Python interpreter was mostly designed with the assumption that the typical use case would be executing the interpreter with a source file as an argument [e.g. python spam.py] and not primarily as an interactive development tool that is left as a running process for days or weeks. Instead Python ships with IDLE which more reflects the idea of an interpreter running source files serially.

Or to put it another way, trace is an option that is passed to the Python interpreter. In Common Lisp (trace ...) is a function that can be called in the REPL.


The standard Python interpreter is crap. One should use IPython. I'd like to see a comparison with that one.


The standard repl for Steel Bank Common Lisp is worse than Cpython's with no basic readline support. The Common Lisp environment really shows its strengths when using a fully interactive development environment like a Lisp machine environment or Emacs with SLIME.


I'd call it the "built-in repl" rather than the standard one, since I don't think it's actually standard for people to use it directly.


CLISP has readline support.


That is exactly correct. Guido van Rossum's original paper on a scripting language called ABC which strongly influenced his later design of Python, was basically a scripting language for system administration tasks on Unix and Unix-like operating systems.


It's extremely different. You have to resort to hacks in Python to reload modules, which has unclear semantics. Python doesn't let you update function definitions while running, or let you add new polymorphic methods to dispatch on while developing. Not to mention interactive error handling isn't as powerful as Lisp's.

Python really has a glorified edit-compile-run cycle. The interpreter loop is nice for quick tests or calculations, but isn't too helpful for the incremental development of your program.


> You have to resort to hacks in Python to reload modules, which has unclear semantics.

This is the crux of the issue. Even in Python 3 the semantics for reloading modules is wrong:

Other references to the old objects (such as names external to the module) are not rebound to refer to the new objects and must be updated in each namespace where they occur if that is desired.

If a module imports objects from another module using from ... import ..., calling reload() for the other module does not redefine the objects imported from it — one way around this is to re-execute the from statement, another is to use import and qualified names (module.name) instead.

If a module instantiates instances of a class, reloading the module that defines the class does not affect the method definitions of the instances — they continue to use the old class definition. The same is true for derived classes.

https://docs.python.org/dev/library/importlib.html#importlib...

In principle Python has a runtime with type-tagged objects that you can inspect and code you can load, so it should be dynamic. Because of the way module and class loading behavior is defined you cannot actually take advantage of this dynamic runtime. Python does not really have a REPL, it has script loading akin to most Unix shells, and a second-class REPL that is useful for some limited debugging. This unnecessary dichotomy imposed on code loading makes it very different from Lisp and Smalltalk.


Common Lisp supports stopping execution of a function in the middle, redefining the function, changing the values of all accessible dynamic and lexical variables, adding new variable definitions, redefining the types of the values, and then resuming execution from the start of the redefined function, or from any other accessible control point.

It also supports writing condition handlers that can do all of the above under program control, rather than under human control. In other languages you would say "exception handlers", but Common Lisp conditions include representations of situations and responses that exceptions typically don't.

These facilities, along with things like a debugger, a tracer, support for built-in documentation, a compiler, and a disassembler, are part of the language standard, not the development environment.

Common Lisp is a language designed from the ground up for writing programs by interacting with them as they run. There aren't very many other languages like that. Smalltalk is one of them.


I've been writing in Common Lisp for 7 years and am working on a python project for ~8 month (for a machine learning task w/ Tensorflow). I am insanely irritated by countless points, to name a few:

* Python REPL does not check and warn the trivial type/arguments errors when a function is defined. It is only after the program reaches that erroneous line that an error is signaled. SBCL always warns about the function signature. Perhaps this may be customized by some global variable or a command line option, but not being warned by default is a huge waste of time, as it takes non-negligible amount of time to reach there (specifically because it is a ML task). (SBCL also warns me about type errors in most of the standard functions as well as ftype'd functions.)

* Python does not have restarts. I mean the condition system in Common Lisp. This again imposes a significant amount of slowdown on the debugging cycle as an error may be signaled only after a multi-hundreds-MB data file is loaded in the beginning of a function and then the erroneous line of code is run. In CL, when an error is signaled, the program enters the debugger and halts there, during which you can correct the program, redefine the function, then resume the code from one or two stacks above the current. In python an error always rewind the stack to the toplevel, so you have to rerun the whole function, loading that huge data file again in the middle of the stack. Don't say you are afraid of some side effects in the code, I follow "Mostly Functional Style" in CL.

* Python debugger shows the stack frame, but does not by default show the values of the arguments in the function call stack when an error is signaled. It forces you to use an ugly printf debugging style. I would like to know if it is possible to alter the way the stack is printed. In CL, a debugger is just a function stored in debugger-hook. I hope you can do something similar.

* Somehow Emacs C-M-f selects the whole line, not an expression.


With CL if you have a bug on your web server you can just connect a repl into the running server, inspect the state of the system, modify that state and update code on the fly if necessary.


I guess the key here for me would be inspecting the state of the system. I tried learning common lisp a few times and it never clicked.

When clojure came around, I tried lisping again, and for whatever reason i was ready for it, and clojure is my goto language.

That said, I'd never connect to a repl on a server and do any kind of bug fixing unless it was really dire. It's too easy to get the run state out of sync with the text source files, and I don't see why this would be different with common lisp.

I think the repl is great for development and figuring things out, but the whole idea of "program as state" seems antithetical to the ideas of functional programming. With the main issue for me being that I have to somehow keep track of this program state in my head once I start tinkering with it.

For example, say I find a function has a bug and I go into the repl on the server and fix it, in situ. Say I spot some other issues, or had them on my list of things to fix when I had time... I start modifying functions here or there. Now my "program state" no longer matches my source code, and I have to remember to fix it so that it matches. That's error prone. I could enforce my own methodology of fixing the source and copy-paste to the repl or send it in whatever way, still error prone.

I have to remember what changes I made, to what functions, and if I iterated, now I have to remember which iteration was final. Multiply that by any and all functions that are touched. It's a lot to keep track of.

So when you say "inspect the state," I have to think common lisp must have some feature for state inspection that is missing in clojure. I know there are a lot of features of common lisp, especially in stack-tracing, that clojure has no parity to. But I'm curious if common lisp has some way of completing a "round trip edit" such that an edit/correction made on a server process, in situ, are able to be easily propagated back to your master source?

If you read all that, thanks :)


> But I'm curious if common lisp has some way of completing a "round trip edit" such that an edit/correction made on a server process, in situ, are able to be easily propagated back to your master source?

In SLIME you just connect to the Lisp process, open up your versioned-controlled file, edit it, hit C-c C-c for slime-compile-defun or C-c C-k for slime-compile-and-load-file. Isn't that how CIDER works also?


True. If you are going purely functional I guess you wouldn't have much state to inspect. Plus in a stateless web server you generally want to avoid keeping state around on the server.

I remember one time, it was actually with Clojure, I had to deal with Paypal. Since paypal only sent results back to the server and not to localhost I couldn't debug the problem locally. So I connected to the server, bunged all the debug information into an atom and made a few requests. Then I could inspect the atom, make some changes and find out what the problem was and fix it right away. It took just a few minutes.

At about the same time I was also working on a C# server. There was a bug there that could only reproduce itself on the server. To fix that bug I had to put a load of logging into the source. Check the file into source control. Wait for the CI system to build and deploy. Then kick off some requests. Inspect the log files. Make some changes. Check in.. wait.. etc.. It took days to fix that bug.

Admittedly the Clojure project was mine on my own server and the C# project was on some corporate enterprise behemoth so there were other factors that hindered that project, but it did highlight to me how much quicker you can get stuff done when you have that instant feedback.

When I say inspect the state, generally I just mean inspecting the values of global variables. In CL it is possible to put breakpoints in the source and inspect all the variables up the stack. Mind you, this can be a bit hairy on the server if there are multiple people connected as there is no guarantee as to whose Slime connection will pick up the breakpoint.

The round trip edit is pretty similar to Clojure. You would generally have your source file open in Emacs, change the source code and press Ctrl+CC with the cursor in your function definition to apply your edits to the server. Then you save the file and check it in to source control. You don't have to type directly into the repl.

You don't have to work like this if you don't want to. Without mentioning any names, it has been known for the server image to be completely out of sync with the state of the source files to the point where there weren't really any source files. The work around was to just treat the server image as the master source of truth. Luckily in CL you can just dump the image to create a new executable with its current state. So for several years this one server existed only as a constantly updated binary image. I wouldn't recommend working like that, but it has been done.

Depending on your implementation - I know Clozure CL at least allows this - you can extract the source code out of the running image. So it is certainly possible to propogate back to master source. It was eventually how this one server was restored to a more orthodox source code tree..


Thanks for the reply. It seems a bit fragile and convoluted to me, but I suppose if you are used to it and are comfortable with the pitfalls it would be fine.


Yes, working directly on the server is definately fragile and fraught with all kinds of dangers. I try to avoid it as much as possible, but it is very handy having the option there as a last resort.


C# edit-and-continue sounds like a pale shadow of what's possible, and it's usually only useful locally!


If you wanted, you could have any bug in a running web server pop up a window in your editor, fix the bug within the call that's buggy, then resume execution from that point at the call stack, successfully returning results to the client. Doing so doesn't necessarily make a lot of sense, but it's a very powerful technique if you choose to use it.


This is true and awesome.

To be fair, though, I run an nREPL TCP listener on my production servers too, so I can do the same thing with Clojure. :)


Not quite. In Lisp, when you get an error, the system pauses and lets you resolve the issue before resuming the program. Sometimes this even means typing in a function really quickly before a request times out (which isn't such an insane suggestion in the context of development). In Clojure, errors like that unwind the stack and tear everything down.


Reminds me of Paul Graham's essay about how at Viaweb, they sometimes used to fix bugs (in the Lisp code) while the customers who reported them were still on the phone, and then the support staff would tell them to check whether they were sure it was a bug, by trying again - and since the bug was now fixed in the server, when they did that operation again, it would work.

I think it was in his Beating the Averages essay - the one in which he talks a lot about the advantages Lisp gave them at Viaweb, over the competition.


You could do that with a PHP site if you're working directly in production. That's not necessarily an advantage of Lisp.


I think I had not described it fully (since had read it long ago). Searched for the article just now. The Beating the Averages article [1] does not have the point I mentioned. But a more detailed article [2] linked from it, does:

[1] http://paulgraham.com/avg.html

[2] More Technical Details: http://paulgraham.com/lwba.html (note: redirects twice)

Here's the relevant section:

[ Interactive Toplevel

Lisp's interactive toplevel is a great help in developing software rapidly. But the biggest advantage for us was probably in finding bugs. As I mentioned before, with Web-based applications you have the users' data on your servers and can usually reproduce bugs.

When one of the customer support people came to me with a report of a bug in the editor, I would load the code into the Lisp interpreter and log into the user's account. If I was able to reproduce the bug I'd get an actual break loop, telling me exactly what was going wrong. Often I could fix the code and release a fix right away. And when I say right away, I mean while the user was still on the phone.

Such fast turnaround on bug fixes put us into an impossibly tempting position. If we could catch and fix a bug while the user was still on the phone, it was very tempting for us to give the user the impression that they were imagining it. And so we sometimes (to their delight) had the customer support people tell the user to just try logging in again and see if they still had the problem. And of course when the user logged back in they'd get the newly released version of the software with the bug fixed, and everything would work fine. I realize this was a bit sneaky of us, but it was also a lot of fun. ]


So it was the interactive toplevel and the break loop [1] that seem to have provided the advantage.

[1] Discussed in this same thread, e.g. this comment by mikelevins:

https://news.ycombinator.com/item?id=14677016

Not sure if PHP has that.

Incidentally, I seem to remember the following: some years ago, after playing around some with Lisp (Allegro CL from Franz, and the Lisp IDE from Lispworks, both of which supported this feature or something like it), I was trying out a then-current version of Visual Studio, and saw that it had a new feature called Edit-and-Continue, which I thought was similar to the Lisp feature.


Good point, see my other comment though.


http://www.apress.com/us/book/9781484211779

For those interested buying a copy of Common Lisp Recipes: I think Edi said that for the author it is financially better to go via his page:

http://weitz.de/cl-recipes/

But then, supporting a local bookstore might also be an option. Though I have to confess, I got my printed copy from Edi as a gift... :-)


If you want to be even more disappointed, have a look at Xerox PARC technical documentation about Interlisp-D, Smalltalk and Mesa/Cedar workstations.


[*]

But why can't we have nice things back?


Like that discussion a while back about early competitors to CSS like DSSSL. Imagine if, instead of CSS/Javascript, we'd just had Scheme. In fact, imagine if instead of HTML/CSS/Javascript... we just had Scheme.


This particular example breaks my heart, because AFAIK[0] JavaScript was supposed to be Scheme at least two times during its development.

--

[0] - read that in the interviews in "Coders at Work".


Scheme: ever the bridesmaid never the bride?


Other than the source code looking nice to people who prefer to see s-expressions everywhere, how would that have improved the web?

Particularly at the beginning, when the web was intended to be nothing but markup and hyperlinks for otherwise plain text documents?


Because contrary to common belief, in capitalism it's not the best solution that wins, but the meanest shark.


That I know. But forgetting the hardware for a bit, at least software (in particular open source) is still (fortunately) a quite economically inefficient space. Yet there isn't much work going on in bringing the good solutions from the old days back.


I think it's not a matter of open source : there's no mean shark behind LISP but there's (a really mean) one behind C# (or Java or Swift etc).


I like to think that JVM, .NET languages and their tooling, and to certain extent Swift (with Playgrounds), are ways to have those things back, even if not 100% the same.


As someone currently learning CL, I think part of the reason it doesn't have widr adoption is that there is a huge number of features.

Consider a language like Python. Aggressive duck typing means consistent syntax for different data structures and types. Stuff "just works" without a deep understanding of how it works.

CL is the opposite. What is the difference between setp, setf, defvar, and defparameter? Why is the documentation so technical and dense? What is a "form"? What are multiple return values and how do I get to them? Should I spend time learning the loop macro DSL?

I find it similar to Haskell in this regard. The base level of knowledge to use the language is so high that you pretty much need a book, or a degree, to be able to get started. That's a huge barrier to entry.


> Stuff "just works" without a deep understanding of how it works.

> The base level of knowledge to use the language is so high that you pretty much need a book (...) to be able to get started.

I wonder what that says about developers these days.

Ever since I started 'till this very day, it was obvious for me that if you want to learn something, you go pick a book about it. Books aren't scary. They have one of the highest knowledge-transfer-to-time ratios out of any sources you could find; they easily beat videotutorials and text tutorials. Most programming books I've read are also quite pleasant and easy to follow (better than some novels I've read...).

Are people scared of books these days?

I mean, I accept that the power of a tool can be a limiting factor to adoption, but I'm deeply saddened by it. Learning and understanding things isn't hard.

--

> CL is the opposite. What is the difference between setp, setf, defvar, and defparameter? Why is the documentation so technical and dense? What is a "form"? What are multiple return values and how do I get to them? Should I spend time learning the loop macro DSL?

The answer to that is simple: go and read a book :). Practical Common Lisp is available on-line for free, and it's a current best introductionary book for CL.

http://www.gigamonkeys.com/book/


> Are people scared of books these days?

Given the amount of video tutorials on YouTube, I would say yes.


Some books are complicated to trace and buy. And computer books have a short-life so in the last years are mostly vanished from local libraries. I had tried to buy lisp books before in dead tree format, online and local, without much success.


> Are people scared of books these days?

No, but books are more difficult to access than online tutorials and more expensive.

If I suddenly decide I want to learn Common Lisp one night, I'm going to Google "learn common lisp" and read/watch tutorials before I buy/borrow a book. That's not to say I won't move to a book eventually, but tutorials are infinitely easier to access.


Besides the fact that one of the most popular modern books teaching Common Lisp is available as an online website for free (Practical Common Lisp), introductions to popular programming languages – including a few other books on Lisp – can generally be readily found on pirate ebook communities. There are some domains where you are forced to "buy" or "borrow" books to learn, but coding is definitely not one of them.


I think most people learned programming by doing stuff, not by reading books. Basic, Bash, Perl, PHP, Javascript all agree with me...


You can't learn programming without doing stuff, but to understand what you're doing, you need to learn some facts - and books have one of the best, if not the best, ratios of learning to time spent.

(In fact I'd be worried to work with programmers who didn't back their experience in their primary language(s) with at least one solid book.)


Yeah, but you're talking about good programmers.

Do you really think that all the others really read books? :)


Programmers who manage to learn a language primarily by using it usually do so because of one of two reasons:

1 - the language was simple enough that there really wasn't that much to learn

2 - that programming language was similar enough to the languages they already knew that there really wasn't all that much to learn

When you're trying to learn something that's radically different from what you already know, and relatively complex, then it's a lot harder to just pick it up by playing with it and using it. That's when you usually need a good book, teacher, and/or course.

I've heard so many programmers brag about how they can learn any language in a weekend, or about how "all languages are the same". Usually they know only one language paradigm: Algol-like imperative languages, and sometimes don't even realize that there are other paradigms out there that are mindbendingly different, and which might actually be challenging to learn if all they've done their life is programmed in languages like C or Java.


> I think most people learned programming by doing stuff, not by reading books. Basic, Bash, Perl, PHP, Javascript all agree with me...

Most people I know who learned programming (including with the languages you list) did so by reading books (or the digital analog) and doing things, and the former was essential to the latter. So, no, I don't think those things are examples which your support your “by doing things, not by reading books” position at all.


Are most Perl programmers ignorant of the camel book? For the others I might agree. For myself, I learned a little bit of HTML "on the fly" (and had the help of a short web series to teach me a few tags, this was before w3schools, or at least before I knew about it) but a friend who was letting me host and FTP onto a subdomain of his shared server noticed I could trivially get rid of a ton of repetition by using PHP just to include a header, write the unique content, and then a footer. He wrote an example for me to apply and recommended a PHP book and that's how I learned programming. If you're a beginner, a beginner focused book makes a world of difference.

I think the main trouble is that it's hard to know what the good books are for your particular needs, and most people aren't going to want to try exploring some alternatives to find a good one, or maybe haven't been given a good recommendation yet, so they don't bother reading books at all unless really curious or forced to... When you're just messing around or just need to edit relatively simple business logic in whatever language without making a bunch of new stuff, you can get away without understanding most things in enough detail that a book would cover.

Lots of books exist for different needs (references, subject-focused, beginner focused, general tour of the language...), so again it's hard to pick out in advance the one you need right now. Some of the more generic language level ones targeted at professionals still manage to leave out crucial sections like "How do professionals actually develop in this thing, and what alternatives are there? What's the simplest way? How are things deployed to production?"

For Common Lisp in particular the problem is two-fold. People get recommended SICP all the time, which is a good book, but they read as far into it as they can and then assume they know Lisp and wonder what the big deal is when compared to modern dynamic languages that let them loop without recursion or use infix math. But if they then go on to try and learn about Common Lisp, so many references (including Practical Common Lisp) don't show the good stuff for way too long, if ever, but begin by saying it's basically a waste of time without emacs. Now the reader has to learn emacs and like it. It's not a waste, yet books don't always address that. On the other hand if you're tool-agnostic like some Lisp books you might give a similar impression of SICP and leave out some of the cool bits because they can be hard to convey in text. The Debugging Lisp series at http://malisper.me/debugging-lisp-part-1-recompilation/ is my favorite at the moment to show people what makes Lisp different. It's emacs focused but it shows you why you might want to use emacs, or some other editor that can hook into the same underlying commands like 'COMPILE that aren't emacs-specific. The REPL is like a constantly present gdb (and all that implies like the possibility of better viewers than the text terminal, such as emacs) but way better.


> I think the main trouble is that it's hard to know what the good books are for your particular needs, and most people aren't going to want to try exploring some alternatives to find a good one, or maybe haven't been given a good recommendation yet, so they don't bother reading books at all unless really curious or forced to...

This is a solved problem, too, but not many people seem to understand the solution until told explicitly. I know it took me a while to figure it out, but here it is:

With the Internet (and little English proficiency), you have the access to the very best material human race created on any given subject. In programming, the best books for topic X can be easily identified by searching for "best books on topic X" and browsing the resulting Reddit and Quora threads. It's literally that simple - if you see some books popping up constantly in such topics with high recommendations, it's a safe bet to go and read them.

RE Common Lisp, I don't think it's that bad, as long as people get directed to PCL instead of SICP. I always recommend SICP as a book for blowing your brains out, and learning 80% of concepts used in programming in general - not as a book for learning Lisp, because it's not about that.

The Debugging Lisp series is awesome, thanks for linking that!


> With the Internet (and little English proficiency), you have the access to the very best material human race created on any given subject.

This is a common belief I hear from people, but it is deeply, deeply flawed.

The Internet mainly has access to the very best promoted material that the human race has created on any given subject.

> and little English proficiency

Of course, it can't be best if it's not in English.


> The Internet mainly has access to the very best promoted material that the human race has created on any given subject.

I disagree, insofar we're not talking about some obscure lone genius work which for some weird reason isn't known to anyone else. I mentioned Reddit and Quora for a reason - communities around specific topics can recommend best sources pretty well, and evaluate them by quality.

> Of course, it can't be best if it's not in English.

Well, for better or worse, English is the dominant language of science and technology today, so it's expected that best resources will be created in, or translated to, this language.


> recommend best sources pretty well

The face value interpretation of your original statement is that not does the Internet accurately recommend the best sources, but it also hosts them too.

"access to the best material" is more than merely "access to reliable recommenders of the best material", right?


Within technical fields it hosts that material, too. Once you know the name of a book it's usually easy to track down a PDF somewhere :).


That might be, but it might not be in the future and meanwhile I'd be weary of drive-by downloads and virus infected warez.

Anyways, you are missing the point, that teaching involves conversation and that can't be easily gotten from a book. Sure, a book is a good supplement, some are called companion, even, and with programming the need for good documentation is called out often enough, but still.


> That might be, but it might not be in the future and meanwhile I'd be weary of drive-by downloads and virus infected warez.

Honestly, I'd call it learning experience on using the Internet ;).

> teaching involves conversation and that can't be easily gotten from a book. Sure, a book is a good supplement, some are called companion, even, and with programming the need for good documentation is called out often enough, but still.

Teaching involving conversation with a good tutor can indeed be even more efficient than books. But that's a pretty rare situation (average university classes don't count - too much people, not enough time, too crappy tutors). Books are a good substitute - in fact good books for a given subject exist exactly so that you can learn something without having a specialist explain it to you face to face.

Companion books is something I'd generally avoid, since they exist to support a class, not to give you information.


Nevermind university, a publicly visible lisp community just doesn't exist, with all due respect, to motivate it's use, books or not. That sounds dismissuve, but Programming should be a social effort in the large.

I guess the point I was trying to make is that patronage at the right time might have a lot more potential to generate motivation, insights, etc. Professors in huge classes are too distant for that, agreed, but one benefit of university is the social experience. Outside of university, stumbling on the right path without guidance would be extremely lucky, while finding a good book on the internet depends on asking the right questions and one single book certainly isn't enough. And most people need guidance on how to work through books, to begin with. Sure, with regards to information technology, the internet hosts the largest community there is to learn from, but arguably it is, again, almost too big for a novice (and might have a lot of negative side effects).


> Consider a language like Python. Aggressive duck typing means consistent syntax for different data structures and types. Stuff "just works" without a deep understanding of how it works.

Really? I've never seen such a leaky set of abstractions as Python's collection library. It's ridiculous how very often I find something irritating and the reason behind it is, "This was the first implementation and therefore was crowned 'the simplest.'" That seems to be the pattern in general with Python.

A great example is Python's complex iteration system. You have 2 ways to deal with it; a sophisticated but very limited list comprehension syntax and a absolutely primitive direct object iterator that barely abstracts over a coroutine.

You mention the cognitive cost of deciding what to learn, but this is actually a split responsibility between the language community and you personally. You need to make that call, and it speaks to a broader phenomenon.

> I find it similar to Haskell in this regard. The base level of knowledge to use the language is so high that you pretty much need a book, or a degree, to be able to get started. That's a huge barrier to entry

The thing that's so irritating to me about this argument is that nearly everyone who has learned to program falls into one of two categories:

1. Someone who spent a lot of time self-starting in the core principles of OO and imperative programming.

2. Someone who took a class or read a book to help get those principles down.

Folks offer objections to going back to novice status after they've done it once. But, Haskell and Lisp offer totally alternative programming paradigms, paradigms that mainstream scripting languages have been watering down and implementing in a limited context for years. It's only natural that there is a lot of stuff there. There was a lot of stuff to learn for OO scripting languages like Python!

And yet, the idea of learning these with the same methods new programmers use to learn OO patterns is panned as "unfriendly to beginners." I think in reality it's only unfriendly to intermediates.


> Most folks actually object to going back to novice status

A good part of that reluctance is entirely rational.

There are many dozens of programming languages that might be worth learning based on what communities of current users say about them.

But you can't tell if they're really worth investing all over again what you already invested in other languages without, well, investing.

It's a lemon market. Sellers know more than you about the true value of the car, err, language, but they have incentive to tell you it's great. Even worse, with programming languages even the most honest sellers might not know how their language really stacks up to the ones you already know because by symmetry they are underinformed too.

For example, I tried getting into Smalltalk based on the intense hype coming from the community, but I came to suspect it really doesn't offer that much of an advantage over languages I know better and stopped. But maybe it really is better... I'll probably never know.


I agree with the risk/reward thing. I think you need to take into account how likely you are to use the language before investing a lot into it.

I ignore smalltalk, ruby, perl, python, all that stuff. I don't want to use them, and somthing like smalltalk probably won't earn me money. Rust and D are similar. I've always thought D was interesting but I can't see it being worth my while to put the effort into it. Rust is just too different to bother with unless it becomes the new C++.

I think swift might be worth a look, as long as Apple doesn't drop it like dylan. It is close enough to what I know that it might not take an age to learn.

CL is good for nerd-cred, and gives a good leg up for Clojure. It probably also has some fundamental lesson to teach different to what I know instinctively. So I keep poking at it hoping it will click one day. Clojure seems like it would be a good thing to be able to claim knowledge of.


I dunno if you find this worthwhile, but I have shipped code in about a dozen languages in my career, with languages like CL among them.

For me, I really don't regret learning any of them, and they all had something to teach me that I've carried forward.

For example, learning Common Lisp really let me internalize the concept of programming a meta-language to make my actual job easier (I later went on at that same job to write a very large, sophisticated code generator for a lot of humdrum deserializers). Learning Erlang helped me realize what a distributed system really is. Learning Java helped me understand what good concurrent threaded programs based around semaphores and queues could really god. Learning C# helped me realize there was a whole another level than that. Learning Clojure helped me realize we could use STM in the real world.

I could go on. But all of these things are things I carry with me to this day. I don't do it for "nerd cred." I do it to grow as a software engineer.


"I do it to grow as a software engineer"

That's why I keep poking at CL, and don't bother much with the others. I don't think I'll get much of a paradigm shift from D or python but if I ever grok CL properly I'll feel I've learned something.


If you just want to extract the juicy bits, may I recommend "Let Over Lambda." https://letoverlambda.com/


I don't understand this idea you're putting forth. Well actually both ideas.

1. That there is a high risk here.

2. That the outcome could be bad.

Neither of these seem true. I dislike Python the way I dislike wearing shoes that stealing too small, but I don't regret learning it. I regret working in it more and more over time.



No I'm aware of these terms. I just don't get how they apply to skills acquisition.

How does learning a language as part of a professional skills acquisition represent significant risk when the alternative you're proposing is not learning any new concepts at all?

Your proposed cost model doesn't work when you're not choosing between N options and rather choosing between selection or no selection whatsoever.

You're probably more interested in modeling it as an OSP.


I can't find talk about "high risk" in what I wrote. I can't seem to find where I'm "proposing not learning any new concepts at all." You seem to enjoy arguing against figments of your own imagination. I can't help you. Have a nice day.


You too, cronjobber. I apologize for giving you the benefit of the doubt and assuming that you weren't just throwing rocks at lisp and smalltalk and was trying to make an actual point about the cost of education.


> giving you the benefit of the doubt and assuming that you weren't just throwing rocks at lisp and smalltalk

Nope, I don't believe you ever gave me the benefit of the doubt—your undoubted assumption that I was just throwing rocks perfectly explains your arguing style ;)


My dude, you just inverted what I said after posting a snippy series of Wikipedia links and you're lecturing me about a bad attitude?

Very well. You cannot help me, indeed.


Yes, Common Lisp has a huge number of features. Luckily you don't have to learn all at once to write useful programs.

Python is definitely a simpler language. At some point it gets more complicated, when you need to move out of the comfort zone. For example last I looked the main Python implementation had a GIL (Global Interpreter Lock) which makes multi-core concurrent execution a problem. Common Lisp has more sophistcated implementations which don't have a GIL and easier use of multi-core threading.

Yes, you need to learn the difference between setting a variable and defining a global variable.

A 'form' is any valid expression in the Lisp programming language:

This is a valid Lisp form: (+ 1 2) It computes to 3.

This is not a valid Lisp form: (1 2 -). Trying to execute it is an error. The list is a valid s-expression, a list with three elements. But it is not a valid Lisp program, because Lisp expects the operator as the first element of a list and 1 is not a valid operator.

This idea of s-expressions and some of them are valid expressions in a programming language is different from Python, which does not have the idea of code-as-data. Thus you have more to learn and it is more complicated. But it makes many forms of computing with code easy.

Yes, you will have to learn about functions being able to return more than one value.

Yes, learning LOOP is useful. It's not the most elegant construct but it is quite powerful. There is a slightly more elegant and even more powerful alternative: Iterate. But it is a library and LOOP is already built-in.

Yes, a book helps. Luckily there are many. See the here mentioned Common Lisp Cookbook.

As introductions see:

Practical Common Lisp from Peter Seibel. http://www.gigamonkeys.com/book/

Common Lisp: A gentle introduction to symbolic computation. https://www.cs.cmu.edu/~dst/LispBook/ You can download the older version for free as a PDF.

Good luck with learning it!


Land of the LISP looked fun when I skimmed it at a bookstore. Teaches by building games.

https://www.amazon.com/Land-Lisp-Learn-Program-Game/dp/15932...


I have it and it's quite ok; very fun to read. I'd recommend it to people who want to jump into Common Lisp and lisps in general, who know at least one other programming language.

That said, it's not as comprehensive as Practical Common Lisp - it covers a bit less of CL, and a bit more of interesting things you can do it.


Appreciate the quick review. Maybe when I re-learn CL Ill use it since I have prior experience. Plus, I just remember it being super, geeky fun in terms of the writing and art.


> Plus, I just remember it being super, geeky fun in terms of the writing and art.

It is! Very fun in art, style and examples used! Has some funny hacks too, like one-page game written mostly in... format.

(And for learning, a particularly useful thing is a two-page loop DSL reference.)


There are a lot of good answers here, but I'm going to speak up as one CL expert who absolutely loathes and avoids LOOP. You can definitely write anything in CL without it.

To me there are different kinds of iterations. Many are naturally written as MAPCAR/REDUCE because the iterations are all independent. Iterations where there's lots of state carried from one iteration to the next are more naturally written with DO or even using tail recursion, if the implementation you're using supports that (most do these days). The theory behind LOOP is that all iterations are fundamentally the same ("one macro to rule them all" I guess). I think this is fundamentally wrong-headed. I want to be able to see at a glance what kind of iteration it is by seeing how it is written.


Let me be a contrary voice since I almost exclusively use LOOP (or ITER) for iteration, mainly because I really don't care what kind of iteration ought to be used[1]. I just want to iterate over elements in a data structure.

If later it needs optimization I'll give it a better look. (I've rarely needed to do this.)

I like my iterations to look the same so the source as a whole will be easier to grasp (for me and someone else).

Then again, you're Scott Burson and I'm just an anonymous Lisper so the parent should definitely give your opinion more weight.

[1] And DO has always looked ready unclear to me.


I don't use ITERATE, but that's more because I haven't felt the need than because I dislike it; it looks much better designed to me than LOOP. Maybe I should give it a try.

As for giving my opinion weight, it's definitely a minority opinion among CLers, but I think we're a significant minority; I don't think I'm the only one.

DO seems a little complicated at first, it's true, but once you understand it, the parts all fit together neatly. The only thing I wish had been done differently is the sense of the exit test -- currently it acts like "until" (the loop is exited when the test form evaluates to true), but the consensus of modern programming languages is that "while" behavior (exiting when the test is false) is more natural.


> Aggressive duck typing means consistent syntax for different data structures and types. Stuff "just works" without a deep understanding of how it works.

  $ txr
  This is the TXR Lisp interactive listener of TXR 180.
  Quit with :quit or Ctrl-D on empty line. Ctrl-X ? for cheatsheet.
  1> [mapcar succ "abc"]
  "bcd"
  2> [mapcar succ #(1 2 3)]
  #(2 3 4)
  3> [mapcar succ '(1 2 3)]
  (2 3 4)
  4> ["abc" 1]
  #\b
  5> ['(0 1 2) 1]
  1
  6> [#H(() (2 two) (1 one)) 1] ;; #H = hash literal
  one
  7> (car "abc")
  #\a
  8> (cdr "abc")
  "bc"
  9> (car "a")
  #\a
  10> (cdr "a")
  nil
  11> (ldiff "abcd" "cd")
  "ab"
However, your understanding has to be deeper to grok how this works. It is simpler for mapcar to just reject strings and be described only as a list operation.

Common Lisp does have the the concept of sequences: a collection of generic operations over vectors, strings and lists. It just doesn't "go to town" with it. Common Lisp offers compilation to fast code. Really fast. Python-will-never-get-there fast.


> What is the difference between setp, setf, defvar, and defparameter? Why is the documentation so technical and dense? What is a "form"? What are multiple return values and how do I get to them? Should I spend time learning the loop macro DSL?

All of those things are there for a reason, and most of them are very well thought out, documented, and are useful. If someone does not like having a lot of tools, they can use a poorer programming language. What kind of contribution do you think that someone with an attitude of "stuff "just works" without a deep understanding of how it works" can make to a community? Popularity and fads only have value to technical book publishers and conference organizers.


"All of those things are there for a reason, and most of them are very well thought out"

From reading about the history of LISP I'm not sure I agree. My take on it is that It was never meant to become a real language as it is now, some guy hacked together an interpreter for the s-expressions because it looked fun and easy, no-one ever bothered to take it further, and here we are.

LISP seems to be the bare mininum required to do whatever McCarthy was starting to explore and these days a lot of LISP nuts seem to think it is the coolest thing ever because of its minimalism without realising it was never meant to be that way.

Yeah, you can express anything in lists. You can also express any program in a turning machine. Doesn't mean you should.

But I don't know LISP well enough to say whether people who are commercially successful with LISP owe it to the language, or they would have been whatever language they use.


Common Lisp is not in any way a minimalist language. Most implementations are compilers, not interpreters.


Yeah, my comment was a bit snarky. At least CL admits side-effects and non-pure functional programming are necessary.

McCarthy obv knew what he wanted to be able to express and process, but the LISP he ended up with after the s-expression interpreter was hacked together was like the assembly language he was going to compile m-expressions down to.

At the time his main alternative seemed to be FORTRAN. It would be interesting to see if he came up with the same ideas with today's computing landscape and keyboards that had more characters than a card punch. You could argue that many of today's languages incorporate more and more LISP ideas as they evolve but it still might look and work a bit different.


I keep bouncing off LISP, but keep coming back assuming there is something I'm missing.

I'm not convinced it is just 'what is the difference between setp, setf, defvar, and defparameter?' I think it is fundamentally different to how I approach programming.

I learned programming back in the 8-bit era, taught myself assembly, etc. I'm almost a hard-coded imperative programmer. I took to C and Java like a duck to water, and I'm guessing LISP advocates think I'm irreparably broken.

I agree with TeMPOraL that picking up a book to learn things is a good way to go and shouldn't be seen as a black mark against a language. The problem these days is that most books are rubbish. Every Clojure book I've seen is just garbage. I'm guessing CL books are better in regards to technical quality because not every average programmer can vomit one up but everything I've encountered invariably harps on about how much better LISP is than every other idea ever, and how concise and elegant it is, and the general smugness drives me nuts.

Practical Common LISP is better than most in that regard but it still falls into the trap about how expressive and concise and elegant and awesome LISP is right before showing 3 lines of code I have to spend a long time digesting.

I assume I'll get it one day, but it's like turning a container ship going really fast at the moment.

I'm wondering if the best way to understand LISP is to write a CL interpreter.


> What is the difference between setp, setf, defvar, and defparameter?

In very simplified terms:

Use defparameter to define a variable at top level (let's call it "global" for simplification.)

Defvar does the same, but if the variable already had a value, then it won't change that value.

Once you define them you can later change the value using setq or setf.

Setf does everything setq does and more. Setf can change the value of "places". "Places" is a great fundamental concept of CL.

> Why is the documentation so technical and dense?

Because Common Lisp is technical and dense. But it is that way for a reason, and as a reward, it "just works" and allows you to do things impossible or really difficult on other languages. It has no limits. CL is dense in features. It isn't a "watered down" language like python (btw i like python and use it). Python is restrained on features (compared to CL) thus there is less to learn. Thus it is easier to learn.

> What are multiple return values and how do I get to them?

Functions can return more than one value. This is not the same as putting many values on a list and returning said list. CL also allows true multiple return values.

This is explained on the CLHS. Use "multiple-value-bind" to get them.


In Common Lisp, setq is just a variant of setf whose argument syntax must be a symbol. However, that symbol may be a symbol macro which expands to a compound form! Through a suitably defined intermediate symbol macro, setq can operate on any place on which setf can operate.


The documentation is dense because it was written by a committee who were creating a common standard that would be implemented by multiple groups. The fact that it's still a good language in spite of that is pretty impressive!

To answer your specific questions, "form" is just a generic term for any sort of lisp code, whether it's an expression, quoted data, or a special form like defun, if, cond, lambda, etc.

Yes, you should learn the LOOP macro, the simpler iteration macros such as DO-LIST, as well as primitive types of loops (a function that recursively walks down a list, for example). You should learn the primitives because you this is how the nicer macros are implemented. The macros are there to save you time, so you'll end up mostly using them rather than writing everything out long-hand.

There is no setp, but setq is the primitive operation of setting a variable whose name is given by an unquoted symbol: (setq foo 42) sets the variable called foo to the value 42.

setf is magic. It generalizes setq to operate not just on variables but on places. A "place" is any expression that would ordinarily access some value. For example, if I have a list called foo, then the third item in the list is (third foo). I can get the value of that item by calling (third foo), and I can _set_ that value by calling (setf (third foo) 42).

defvar and defparameter are not very different from each other, except that defvar assigns the initial value to the named variable only if the variable isn't yet bound, while defparamter always sets the variable to the inital value you give. This means that if you load a file that has (defvar foo 42) in it, the foo variable will have the value 42. If you then change that value (by running (setf foo 24), for example), and then reload the file, the defvar form in the file won't overwrite the value you've set foo to, while a defparameter would.

For these kinds of questions I recommend the Common Lisp HyperSpec: http://www.lispworks.com/documentation/HyperSpec/Front/index...

The Permuted Symbol Index is usually where I start; it a sorted index of all the symbols, but symbols show up once for each word in their name. For example, if you go to the "I" page of the index (http://www.lispworks.com/documentation/HyperSpec/Front/X_Per...) you'll see that IF is listed, as you would expect, but also FIND-IF and MEMBER-IF. This is super useful, because you might only partially remember the name of what you're looking for, or you might be interested in these other related items that would have been hidden away in a normal index.


The permuted symbol index is my homepage. Best thing since sliced bread.


I'm always confused when nobody else ever has a permuted index, even when they have thousand-word identifiers.


Keep using Common​ Lisp and unite with us aliens on #lisp at freenode IRC!!


That title is in O'Reilly's online Safari subscription library, for those who have an account.

No affiliation -- just checked for myself.


Oh hai Hacker News!

vindarel on Github has been doing an enormous amount of contributions in the past two weeks, and the modern look and feel (and some of the more modern content bits) are entirely due to their hard work.

eudoxia0 spent some time prior to that converting it and getting it modernized.

I'd like to congratulate them on their work, since we're on the front page of HN.

(I sort of steward the GH fork of the cookbook these days).


A Standard. One thing that has not been mentioned here. The lisp community came together and created a standard.

I maintain code from the 1980, part of which is in Common Lisp and part of which is in C. The C code breaks. The latest failure seems to be caused by the GCC compiler changing the semantics of 'inline'. The include files differ between platforms.

The Lisp code just runs.

There are no arguments like Python 2.7 vs Python 3.x. No arguments about C++11 vs C++14. No arguments about Clojure from 4 years ago and Clojure today. No arguments about any of that.

There is a standard. ONE standard. It only matters when you have to maintain someone else's legacy code. But it really matters.


Well, amongst Lispers we do argument a lot about the missing or overarchitectured bits. The ffi, threads, the conditions system, the MOP, ... But only the various FFI's pose a minor problem. It's complaining on a very high level.


Probably the most difficult area in the CL standard is handling pathnames. There are so many possible pitfalls. FFI is an area that, despite best efforts, is likely not a good candidate for inclusion in a standard. It would be better to have all of the implementations converge on a convention since the underlying foreign systems will change over time. Not everything that "just works" has to be in a standard.

C++ issues a "new standard" every 3 years or so which is just nonsense. Even Stroustrop said that the "new C++" is not the "old C++". They are adding ideas (e.g. Concepts) that virtually no one is asking for or knows how to use. Ten years from now that C++ maintenance job you have will be a nightmare. PL/I tried to be everything to everyone for every task and eventually disappeared. C++ is on the same long-term death march. You heard it here first.


Started looking at getting into Racket and CL after reading up on lisp and there is something about it that really bugs me, in a good way. Sadly the train seems to have left the station regarding positions where you use lisp where I live. All is java and C-flavors.


> All is java

Possible tip: sneak in ABCL when they're not looking. ABCL is a Common Lisp implementation for JVM, with (what seems like) relatively OK interop between Lisp and Java code.


Something makes me think they'll notice all the parens during a code review...


Write a reader macro that treats all [ ], { } and < > as if they were ( ), and the only thing they'll notice is that there's less parens/brackets than there should be in regular Java code...


There have been efforts to do something similar to create more "readable" S-expressions. I did work in Common Lisp for a number of years and didn't have any issues with the syntax but I rather like this idea:

http://readable.sourceforge.net/


why would one do that instead of using clojure?


Because as 'klibertp wrote, they're two vastly different languages, with different design decisions.

The whole thread is about primarily Common Lisp; people who enjoy the design choices made by the CL standardization committee and subsequent evolution of the language's ecosystem would most likely want to know that there exists a proper CL implementation for JVM.


but the person i replied to replied to a commenter lamenting the fact that there was nothing but java and c style development jobs instead of lisp inspired jobs. they weren't lamenting the lack of jobs specific to just common lisp. and to me, it doesn't make sense to "sneak in" an esoteric implementation of lisp on the jvm when clojure already exists. what makes people think that would be easier to sell at a place using java heavily?


I wouldn't call ABCL esoteric; it's as rightful JVM citizen as Clojure or Scala. That said, I agree it's much, much less popular in the JVM world, so it could be a harder sell.


Because, despite both being Lisps, Clojure and Common Lisp are vastly different languages, with different pros and cons.


of course they're different. i didn't say or imply otherwise. the original parent mentioned both racket and common lisp, again different languages, so it was clear they weren't looking just for common lisp and were more generally wanting for some lisp or scheme type of job. and i was asking a legitimate and honest question, as i see ABCL to be a much more esoteric and hard sell than clojure to a place heavy on java development.


You asked, "why would one use [ABCL]?" What I implied in my response (sorry for not stating it directly) was that one may prefer the particular feature set of CL over Clojure's.

As for being a hard sell - my personal experience is that if you're in a position that allows you to choose your implementation language, in most cases you can choose Brainfuck and nobody will care. As an example, I introduced F#, Erlang, Elixir and a bit of Prolog to a Python shop that way: all that mattered in these cases was whether the code worked, and it did, so no one complained. The Erlang microservice actually outlived the project it was written for by a year and a half.

On the other hand (still, in my experience) if you're not in a position where you can choose your language you're basically screwed, and should stop trying to convince your team to switch to something else. You have almost no chance of succeeding and a high chance of disrupting teamwork by pushing your opinions on others.

So I was thinking about the former situation, where you have some degree of freedom of choice and can decide the language on technical merits alone.


Common Lisp is vastly more powerful and better implemented.


Seems dead


> Seems dead

The latest release (1.5) was earlier this month.


plus:

it's common lisp. if the language spec hasn't changed, and your code works, you don't really need to change the interpreter.


ABCL's interoperability with Java is not part of the CL spec, but an add-on. This can make it easy to quickly get things done in a situation where Java libraries are the best tool for the task, but at the cost of portability between CL implementations.


Or just use Clojure, which is a nice Lisp (not CL) for the JVM


> which is a nice Lisp

Well, it's definitely a Lisp; however, I don't think the "nice" qualifier fits here.

Clojure has a few interesting features which are well integrated with each other, but it also comes with a lot of opinionated choices one might not like. Another thing - not a language issue per-se - is that relying on Java ecosystem can be painful (and ClojureScript, fortunately finally bootstrapped a while back, is not quite the same language).

Personally, I feel that, in pursuit of simplicity, Clojure dropped a lot of useful features. As an example, in both CL and Racket, you have 2-3 times more ways of structuring your code than in Clojure. This is important, because different parts of a codebase benefit from different structuring mechanisms - and the more choice you have, the easier it is to find the right one. In Clojure, all you have are maps and defrecord, with the latter being almost never used.

Of course, you have macros to combat this, but then you're put in the shoes of language inventor and implementer, which is a lot more complexity than most programmers would like to fight just to be able to order the function definition the way they want.

To summarize: in my opinion, Clojure did some things right but then got locked into that single track and refused to incorporate additional features (and lessons learned) from other Lisps. So no, I don't think it's a "nice Lisp." OTOH, it may well be a good language, especially if your problem and ___domain are a good fit for it.


It might be a nice language but not an especially nice Lisp. Lots of stuff from Lisp is missing: interpreter and compiler integrated, self hosting compiler written in Lisp, condition system with interactive error handling, nice object-system like CLOS, simpler numeric tower, good error messages, Lisp-like stack traces, image-based development with fast startup times, Tail Call Optimization, continuations, ... plus it has some strange design decisions with basic list data structures.

It's 'Lisp'-like where the basic primitive list processing has been replaced with a different higher-level data structure (persistent sequences) built on top of an alien infrastructure which leaks through everywhere and which was not developed for interactive programming (the JVM)... That was one of its original design decisions: being hosted on top of the JVM.


> It's 'Lisp'-like built on top of an alien infrastructure which leaks through everywhere...

Given it's Lisp (CL in particular) that's "built with alien technology"™, I'd say Clojure is built on top of terran architecture, with the usual human stuff leaking everewyhere... ;).


Would Common Lisp be Zerg or Protoss technology?


Xel'naga.


> some strange design decisions with basic list data structures

What are those?


Hopefully a Clojure user will correct me, but something like (1 2 3) has a single printed representation for slightly different data structures. You can construct something which prints as (1 2 3) and when you read it, then it is something else.

  user=> (list 1 2 3)
  (1 2 3)
  user=> (list? (list 1 2 3))
  true
  user=> '(1 2 3)
  (1 2 3)
  user=> (list? '(1 2 3))
  true
  user=> (cons 1 '(2 3))  
  (1 2 3)
  user=> (list? (cons 1 '(2 3)))
  false
It might not be a big deal, but from a language design view I would expect the single most important external data structure representation in Lisp - a list - is not overloaded with different data structures, where basic operators give different results. I personally would also think that in Lisp the predicate list? is fundamental and returns true for everything which looks printed like a list.

Common Lisp has a related problem for arrays: there are some attributes of arrays which have no printed representation. One will read an array back, but it might lack some features - like not having a fill-pointer. But atleast ARRAYP will still return true.

This seq/list thing might not be a big deal in practice, but it's another design wart. The language has in several places names, which are fundamental in Lisp and which mean something totally different now. Like 'atom'. Since the concept of a Lisp atom no longer applies - remember we have seqs, not linked conses - the name was reused to mean something different. atom used to mean (not (consp thing)) now it is something else.


The way I usually read Clojure,

   (1 2 3)
isn't the printed representation of a list. It's just the printed representation of a sequence, which could be a list, a lazy seq, or others.

I'm not a Clojure expert, but when I use it, it looks like everything that responds to seq? prints in brackets. And so I tend to think of a seq as the fundamental, most important data structure in the language, and think of a list of just one kind of seq. And following from that, the seq? predicate is fundamental and returns true for everything that prints like a seq - which includes lists.

So maybe this seq primacy means it would be more accurate to call Clojure "Lisp like" or "a Lisp descendant" rather than "a Lisp"? I'm not sure.

I like Clojure, I like CL, and I tend to use and enjoy them both without worrying too much about taxonomy. Having said that, it's totally fair and important to ask the questions you asked!

For me, though, the answers to those questions don't make me like or dislike Clojure or CL more or less relative to each other. They're both fun and useful, and I'm glad they both exist. :)


Clojure isn't Lisp because it doesn't run Lisp programs and has different syntax and semantics to Lisp. McCarthy's original Lisp, Lisp 1.5, Maclisp, Emacs Lisp, Zetalisp, Common Lisp, EuLisp, etc, can share nontrivial programs written for each other with little to no modification; that's why people talk about them as versions of the same language. That doesn't hold at all for Clojure.

That isn't a complaint about Clojure at all, Clojure is a really cool language and does some things better than Lisp, but it's just not a Lisp, it's a Clojure. It borrows some syntax from Lisp in the same way that Java borrows some syntax from C, and it makes about as much sense to refer to Clojure as Lisp as it does to call Java C.

If you want to say that Clojure uses sexprs for source representation, say that; if you want to say that Clojure has dynamic typing and lexical closures (the latter of which most Lisps have not had), say that. Calling it "Lisp" to mean whatever handful of features that you personally think are the defining elements of Lisp doesn't aid clear discussion.


That's a reasonable way to look at it, and I think we mostly agree. I personally don't call it anything other than Clojure. :)


Call it Seqp. ;-)


Works for me. :)


Thanks! Super interesting.

Of course the meaning of (not (consp thing)) for atom is classical, but don't you think it's a bit weird? It always struck me that way. In a language where lists were the only composite data structure (i.e. not being a cons means being a number or a string or a symbol), it made sense, but that meaning degrades once you have vectors and hashtables. Those are no more deserving of the name 'atom' than lists are. Indeed if one indulges in the oxymoron of treating atomicity as a spectrum, vectors feel more atomic to me than lists while hashtables feel less atomic.


Older Lisps didn't tend to have strings as a separate datatype, they just used symbols and there was a function EXPLODE (and similar) for turning a symbol into a list of one-character symbols (or fixnums), and a corresponding IMPLODE (and similar) for combining them again, and that's how string processing was done. Vectors and hashes predate "real" strings in Lisp as far as I'm aware.


The older Lisp (60 and before) talked about 'atomic symbols'. ATOM checked whether something is an 'atomic symbol'. Cons cells were not atomic symbols. They were pairs. But atomic symbols could be symbols, numbers, functions and variables. Arrays were not there in an explict form and the few data types of Lisp were made of atomic symbols. A number was an atomic symbol where the name starts with a digit and the symbol's assoc list stored the numeric value...

See: http://www-formal.stanford.edu/jmc/recursive/node4.html


I think you will find that Clojure offers many of things that you like about Racket or CL, while also getting rid of a lot of the complexity of CL (CL is not really a functional language, and hardly can be considered an immutable one).

Clojurescript, which is nearly identical semantics to Clojure, also is quite a practical skill for front-end development.


> (CL is not really a functional language, and hardly can be considered an immutable one)

It's important to note here though that this is a feature, not a bug. CL is a multi-paradigm and extremely pragmatic language.


I usually quip that Common Lisp and Perl are basically the same language, bit with different syntax. Not quite correct, obviously, but both languages give me that same pragmatic feel where having several approaches to the same problem is considered something desirable, not a defect.


perl also grabbed a lot of things from CL, like moose(their MOP, at least so I hear).


> this is a feature, not a bug

1) I never said it was a bug. 2) Whether CL's multi-paradigm nature is an advantage or not is subjective.


That's all what I meant. I read your comment as implying this is a bug; seems I was wrong.


The FSet functional collections library [0] greatly improves CL's support for functional programming.

[0] https://github.com/slburson/fset


> positions where you use lisp where I live

Where do you live?


Hi, don't mean to interrupt anyone, just wanted to ask: why hasn't anyone made the common lisp hyperspec a prettier, more navigable and tooling friendly yet?


The license doesn't allow that.

Yes, that's dumb.


See the CL UltraSpec for work in progress:

http://phoe.tymoon.eu/clus/doku.php

It's based on the last pre-ANSI CL standard sources.


I've never ventured into a lisp like language. Would you recommend starting with common lisp or is jumping into clojure ok?

I have been in the JS, Java, Python world for a while and this looks like a language that could stretch my brain a little bit.


I'd probably recommend Racket because it is a complete ecosystem in a single download and has many design elements that facilitate onboarding beginners. This is due to Racket (and Scheme in general) having forty plus years of history as teaching languages [though 'teaching language' does not mean a lack of power or unsuitability for production...e.g. Hacker News is built on the Racket ecosystem].

None of this is because Common Lisp is a bad language. However, it does show its age in so far as its approach to documentation and user expectations. Using Common Lisp is similar to using the Unix/Linux command line. The expectation is that the user is willing to read text files that read like man pages and work their way through deep technical documentation...or to put it another way, it is very common to use EMACS when coding Common Lisp [though commercial editors are available].

To me, Clojure reflects a lot of Common Lisp's assumptions about the user being a professional programmer...to the point EMACS is pretty much the default programming environment [though other environments are available that reflect Clojure's Java heritage].

Since I mentioned EMACS twice, EMACS Lisp is another Lisp that is well documented and easy to install (in addition to being a road to EMACS). While it may be called 'a scripting language', on contemporary hardware that doesn't mean much. Just about anything a person would write for learning will run sufficiently fast within EMACS using EMACS Lisp.


The forty years of a teaching language comes with a cost: zillions of incompatible implementtions and a language which was designed for teaching, retrofitted with more advanced stuff on top which integrate not very well.

Common Lisp was designed early as a more practical language.

Take for example error handling: Common Lisp has a nice error handling system integrated into the language since the mid 80s. Scheme never had something comparable specified in its standards. Sure you can add error handling on top, but it won't be integrated well into the wider language.

There are many aspects where Scheme was underspecified for decades and efforts to redesign the core language were slow, controversial and not really providing nice results. Many Scheme designers wanted the language to be kept as small as possible (R5RS level). The R6RS design was controversial and R7RS 'large' is a very slow effort...

In many ways, Common Lisp is as a practical Lisp language better designed than the average Scheme and Racket is just another Scheme going in its own direction. Actually if you read the Racket docs, it is a very complex system.

One also could use the excellent Chez Scheme (which now is open source) or Chicken Scheme.

The ideas of Racket onboarding beginners is also confusing: multiple languages with some integration mechanism with various degrees of different features and semantics. Much of that is not really needed, but a nice experiment in language modularity. Added to that an IDE which favors batch programming for beginners... In a teaching setting it helps reducing side-effects ... each 'Run' is a new program start.


To me, Racket does not suffer from the issues of incompatibility that occur with Scheme as a general family of languages because cross platform compatibility is sought at the ecosystem level rather than at the level of each individual user source code file. That is why I recommended the Racket ecosystem specifically rather than making a vague reference to Scheme.

To be clear, I think Common Lisp is a great language and in no small part this is due to the stability that its design by consensus and reference standard over approximately a decade by a group of seriously competent programmers and computer scientists. The price of Common Lisp is the RTFM tradition of onboarding users, TANSTAAFL.

I agree that Racket ecosystem is complex. There is a valley that contains the basic abstractions of Erlang [1] and Algol Mesa is accessible right out of the box and Scribble for writing documentation. Of course, the fact that all that complexity is documented in the standard distribution (and generally very well documented) is [at least for me] a feature not a bug.

Finally, my current take is that 'easy' onboarding of users is a bait and switch for any programming language [2]. Eventually there is an inflection point where the answer is RTFM. It just comes earlier or later in different language communities. With Common Lisp it comes much earlier than is typically expected by programmers today. StackOverflow is more likely to be a first attempt at problem solving than Steele's Common Lisp the Language.

[1]: http://docs.racket-lang.org/guide/parallelism.html#%28part._...

[2]: http://norvig.com/21-days.html


> Racket does not suffer from the issues of incompatibility

Just pick any language implementation exclusively -> no more incompatibilities.

> With Common Lisp it comes much earlier than is typically expected by programmers today. StackOverflow is more likely to be a first attempt at problem solving than Steele's Common Lisp the Language.

Well Steele's Common Lisp the Language is outdated anyway. Today one uses the ANSI CL standard, which is less confusing than CLtL2, which was documenting a language in flux.

Actually I don't think Common Lisp users will need to refer to documentation earlier than Racket or any other complex programming system (Java, Scala, Haskell, ...).

I have seen now a lot of people coding Java and I can tell you that documentation-less attempts at development don't work well. Many of the programs and coding styles I have seen are just horrible. It's actually kind of sad. I have seen senior Java developers not knowing how the language works at basic levels, even though they claim to work with complex frameworks.

Recently I have interviewed a professional Java programmer with a few years experience and he had lots of free time at work. He could have read some books to learn something instead of looking out of the window, but he said that he wasn't interested in computer science anyway... though it might have been a joke, I fear it was serious... Anyway, he was a nice person and had some potential.


You are kind of right, the problema is when that great library that would solve your problem that should already been written last week, doesn't work with your implementation of choice.

This is one of the reasons I started to only use vendor SDK languages for production code, in spite of the languages not being as good as some alternatives.


There are a host of good reasons not to choose Racket. An intent to explore the Lisp family of languages is not among those within a standard deviation or two of the median. Likewise approaching the Lisp family of languages with tooling expectations in the fat part of today's Bell Curve probably makes Common Lisp a more frustrating tool than is necessary. In different circumstances, the value of tradeoffs would be different...for example reading an ANSI Standard might be perfectly acceptable as an alternative despite being on the long tail of ordinary practice for someone coming from "the JS, Java, Python world" -- with particular attention to the ECMAscript standard.


I'd skip both of them and jump to Scheme. I've found Scheme to be far more elegant than Common Lisp or Clojure, far more modern and less clunky than CL, and you don't have to deal with the JVM or Java as you do with Clojure.

Chicken Scheme is my favorite, but Racket or Guile would work too. Guile is good because Emacs is slowly transitioning to using it, and Racket for its great ecosystem.

Chicken Scheme itself compiles down to C, so it's quite fast, portable, and light-weight. It also has a great ecosystem, library of eggs, and community.

Others have mentioned Elisp, which emacs uses, and that's good too if you are interested in emacs. It would just be a shame to start with it and get the impression that that's the best that a Lisp-like language can offer when it's really rather primitive compared to a modern Scheme, Clojure, or CL. I myself learned Elisp last, after already learning CL and Scheme, and found doing so to be very easy after having learned the others. So I'd really recommend starting with one of the others and learning Elisp last, if you're in to emacs.


> Guile is good because Emacs is slowly transitioning to using it

Emacs on Guile is nearly a one-man project that has been progressing in fits and starts, and it's not guaranteed that that branch will be adopted as the Emacs mainline. Basically, the Emacs devs are telling the Guile folks that the onus is on them to make something worth integrating into the Emacs core, and so far it's nowhere near that.

But it's important to realize that Emacs on Guile is only about using Guile as the runtime for Emacs Lisp, as multiple languages can compile to Guile bytecode, not just Scheme. Even if Emacs on Guile happened, the user would not program in Scheme, but still in Emacs Lisp.


"Even if Emacs on Guile happened, the user would not program in Scheme, but still in Emacs Lisp."

Well, you could program in eLisp, and most people probably would, but you could also program in Scheme. Most emacs programs would remain in eLisp for a long time, though, as it'll take a while to convert them all over to Guile.

So, yes, this is a long-term project, and most people would continue to program in eLisp for the foreseeable future, but some would program in Scheme and try to convert existing eLisp programs to Scheme.

That's my understanding of the situation, anyway.


What is interesting about eLisp is that Emacs provides an environment where there are lots of low hanging reasons to write a little code in eLisp in order to tweek the Emacs editor to do something new...there's never a shortage of small, interesting, and relevant projects/problems at hand to address in eLisp (and the tooling for tackling that class of projects/problems is very good and always at hand).

For exploring Lisps in general, such 'strange loops' around building tools do not seem quite as obvious obvious or natural. Particularly at small scale programs.


My name is in this cookbook, so it's really old. Only a Common Lisp is a Common Lisp, Clojure not, Clozure yes. To stretch your brain also read SICP https://mitpress.mit.edu/sicp/full-text/book/book.html


If you want your brain stretched, I recommend Common Lisp.

The #1 issue for folks in my experience had been getting an environment set up. Emacs, SLIME, encodings, paredit, ... . It's a big barrier to entry.


I agree (I know I had my issues with Emacs and SLIME when I started; being on Windows didn't particularly help).

That said, for people who want to start with CL now, I'd recommend grabbing https://shinmera.github.io/portacle/. It's a portable Lisp IDE based off a pre-configured Emacs + SLIME + SBCL + Quicklisp + Git combo. I can confirm that it Just Works, out of the box.


Portacle is a great project. My concern with it as a recommendation is that Lispbox was a great project too, until it wasn't...or rather that the enthusiasm of the maintainers of easy to use Common Lisp projects seems likely to be shorter lived than Common Lisp (though perhaps Github may make this less a potential problem). The fact that people get paid to improve and maintain the Racket ecosystem as part of their day jobs in academia is one of the reasons I recommend it for 'how should I start exploring Lisp' (versus learning Common Lisp).

https://common-lisp.net/project/lispbox/


> the enthusiasm of the maintainers of easy to use Common Lisp projects seems likely to be shorter lived than Common Lisp

Not surprising, given that Common Lisp itself is pretty much immortal compared to the typical lifetime of things in our industry :).

That said, I know what you mean. I started with Lispbox years ago, and it was already feeling its age back then. I agree with your observation. Sadly, the commercial CL scene seems pretty weak - the two big players (Franz and LispWorks) seem to mostly keep to themselves and focus on the commercial tools they offer.


One of the 'legacy' features of the Common Lisp community is that it is socially acceptable to charge $2000 per year per seat for software. Though it is not something that I personally have a problem with, my view that paying for software is ok is not all that mainstream these days (imagine paying for a web browser).

What I like about Common Lisp is that it is not in flux and so when I Google up a question, the information I get may be ten years old but it is highly likely to be correct. The price is that the format of the information may be more like a technical textbook than a blog or a StackOverflow answer.


Portacle looks very nice. Is anyone here aware of a similar plug & play Emacs-based IDE for Clojure?



I know about cider, but what I'm looking for is a decent, quick download & click to run environment that will let me play around with Clojure. Something you could hand to intro to programming students and have them writing code in a repl in sixty seconds.


Haven't followed its development for a long while but Light Table might be what you're looking for: http://lighttable.com/


I'll check it out, thanks!



This looks nice & I'll check it out, but at first glance it doesn't seem to be as simple as what I'm looking for:

"For Leiningen users... Add it to your ~/.lein/profiles.clj:"

I truly want something that's not for "Leningen users" (or "Boot users") but for people who've never heard of any of those things and just need to start learning how to write code (in Clojure).


Roswell solves this problem completely.

Quick lisp solved it mostly, but Roswell is the finishing touch in my opinion.

https://github.com/roswell/roswell


Just any text editor and reloading the file one's editing works fine as well:

    shell$ sbcl
    [... some editing ...]
    sbcl> (load "file.lisp")
As for which Lisp. I advise Common Lisp since it's the most practical of the lot. Doesn't force you into a particular paradigm.


No entry barrier anymore with Portacle. Download Portacle, it's a ready to use CL environment, which includes Emacs, SLIME, and the SBCL compiler. Works great! Zero configuration!


a bit late, but we also have an Atom plugin: https://atom.io/packages/atom-slime !


I learned scheme and emacs lisp before starting to use Common Lisp and then Clojure. There are differences between all of them but there's a big enough overlap you won't be wasting your time if you end up switching between them. Clojure is nice if you're already used to working with the JVM


At the end of the day, it depends on what you are jumping in for. Either language will likely be fun and can get some work done.

If you are only interested in getting into the syntax. It almost literally doesn't matter. If you are interested in learning a lot of tricks that you've probably not had a lot of exposure to, CL is almost certainly the more bountiful place to start.


Common Lisp is great. :)


As an alternative to the traditional answers here, consider trying Lumen Lisp:

https://github.com/sctb/lumen

It's self-hosted in JavaScript and Lua. It matches the JS runtime as closely as possible, so if you're comfortable with JS, Lumen is basically a wrapper around it.

Think of it like CoffeeScript on steroids.

There isn't a lot of documentation, but if you have any questions I'm happy to answer them.


Hi @sillysaurus3, I'll bite.

Lumen looks interesting and I dug up some HN comments from over the years.

Any example apps or sample code anywhere?

What about host language access? I'd rather not give up using existing JS and Lua libraries.

I can see there's REPL, but is it possible to have dynamic evaluation while running inside web browser?

Have you used this inside Nginx's Lua module and/or OpenResty?

Why not Python as one target host language? Lists and hashmaps (dictionaries) are pretty prevalent there and I believe continuously developed Lisp dialect is still missing.

Thank you for this. I learned new things already.


I can see there's REPL, but is it possible to have dynamic evaluation while running inside web browser?

Here's a version of Lumen you can copy-paste directly into your browser console:

https://gist.github.com/anonymous/c8e26607668dd6240f606fc2d0...

That's not too convenient, but it's easy to create a wrapper around these building blocks.

If you have any other questions at all, please don't hesitate to ask. I love talking about this stuff.


>> I can see there's REPL, but is it possible to have dynamic evaluation while running inside web browser?

> Here's a version of Lumen you can copy-paste directly into your browser console:

> https://gist.github.com/anonymous/c8e26607668dd6240f606fc2d0....

> That's not too convenient, but it's easy to create a wrapper around these building blocks.

I got Lumen evaluation going on page load with this and it was surprisingly easy!

>If you have any other questions at all, please don't hesitate to ask. I love talking about this stuff.

It very late here, have to try and get some shut eye. I'll be playing with Lumen more now that I got over the first step, thanks to you. I'll be checking this thread and Lumen's repo soonish, I hope.


Also if you shoot me an email, we can chat more easily.


Sure thing. G'night!


> What about host language access? I'd rather not give up using existing JS and Lua libraries.

Easy:

  $ npm i leftpad
  $ LUMEN_HOST=node lumen
  > (require 'leftpad)
  function
  > ((require 'leftpad) "foo" 10)
  "0000000foo"
> I can see there's REPL, but is it possible to have dynamic evaluation while running inside web browser?

Yes, the entire codebase is only 3k lines. You can use browserify, or simply concatenate all the JS files together. Lumen is unopinionated.

I've done this before, and I'm more than happy to set you up with a copy-pastable solution if you want.

> Have you used this inside Nginx's Lua module and/or OpenResty?

No, but there's no reason it wouldn't work. Here's an example of it working in a WASM Lua implementation: https://news.ycombinator.com/item?id=13900453

> Why not Python as one target host language? Lists and hashmaps (dictionaries) are pretty prevalent there and I believe continuously developed Lisp dialect is still missing.

I've spent time on this! It's a difficult problem. In Lua and JS, you can attach keys and values to arrays. In other words, an array can also be a hashmap. Not so in Python -- or rather, not with the standard Python dict() type. And then you run into problems of interoperability and performance: if it's a non-standard type, other libraries run into problems consuming it. This is less true of Python than other languages, though, so I've been trying to find a happy medium.

There are ways of using solely arrays, but it involves having some sort of Keyword type. A normal lisp has those, and typically they use '(a b :x 42) to create an "array" where the first two elements are "a" and "b" and the key "x" has value 42. You can do something similar for Python, and it's an active area of research for me.

> Any example apps or sample code anywhere?

This is Lumen's strength and weakness. Very few people know about Lumen because of a lack of examples and documentation. However, it's incredibly cool and quite mysterious, and I like that it has a very small following. It makes changing the language much easier.

Here's an extensive example: https://github.com/sctb/motor

Note that it won't run on the latest Lumen, because dot syntax was recently removed. You'll need to revert to commit `c146de`. But you see how easy it is to morph the language into whatever shape you want, and how powerful it can be.


>> What about host language access? I'd rather not give up using existing JS and Lua libraries.

>Easy:

  $ npm i leftpad
  $ LUMEN_HOST=node lumen
  > (require 'leftpad)
  function
  > ((require 'leftpad) "foo" 10)
  "0000000foo"
Huh. It just worked. With rlwrap this feels (at least at the very first try) regular REPL!

>> I can see there's REPL, but is it possible to have dynamic evaluation while running inside web browser?

> Yes, the entire codebase is only 3k lines. You can use browserify, or simply concatenate all the JS files together. Lumen is unopinionated.

Amazing. That sounds very compact to me. I'd imagine today's JS build systems can do optimized builds without too much trouble. Also, by that count it should be an afternoon's read to get it.

> I've done this before, and I'm more than happy to set you up with a copy-pastable solution if you want.

My answer in your other comment.

>> Have you used this inside Nginx's Lua module and/or OpenResty?

> No, but there's no reason it wouldn't work. Here's an example of it working in a WASM Lua implementation: https://news.ycombinator.com/item?id=13900453

Ok, must do some testing. I have nothing against Lua, but having a Lisp within Nginx/OpenResty just seems more fun.

>> Why not Python as one target host language? Lists and hashmaps (dictionaries) are pretty prevalent there and I believe continuously developed Lisp dialect is still missing.

> I've spent time on this! It's a difficult problem. In Lua and JS, you can attach keys and values to arrays. In other words, an array can also be a hashmap. Not so in Python -- or rather, not with the standard Python dict() type. And then you run into problems of interoperability and performance: if it's a non-standard type, other libraries run into problems consuming it. This is less true of Python than other languages, though, so I've been trying to find a happy medium.

> There are ways of using solely arrays, but it involves having some sort of Keyword type. A normal lisp has those, and typically they use '(a b :x 42) to create an "array" where the first two elements are "a" and "b" and the key "x" has value 42. You can do something similar for Python, and it's an active area of research for me.

Yeah, I realize there's all sorts of pitfalls. I thought about 3.6 having by default ordered dicts could make things easier. But probably not. Perhaps if I'd do array indexed access in Python with a helper method "transpiled" where necessary. In my own code I could keep from using other, harder to support Pythonisms.

>> Any example apps or sample code anywhere?

> This is Lumen's strength and weakness. Very few people know about Lumen because of a lack of examples and documentation. However, it's incredibly cool and quite mysterious, and I like that it has a very small following. It makes changing the language much easier.

> Here's an extensive example: https://github.com/sctb/motor

> Note that it won't run on the latest Lumen, because dot syntax was recently removed. You'll need to revert to commit `c146de`. But you see how easy it is to morph the language into whatever shape you want, and how powerful it can be.

I'll check it out, thank you. And thank you for this!


I use rlwrap too. There are a few extra options that are nice, so I wrap it up in a script I call `lwrap`: https://gist.github.com/anonymous/c1a0c9d54bc647179fa32c08e7...

Then I invoke it like `lwrap lumen`. I use multi-line mode pretty often, and I have this in my ~/.inputrc file:

  "\e\C-m": rlwrap-call-editor # ALT-RET
So at Lumen's prompt, I usually type () followed by Alt-Return, which drops me into MacVim and I edit from there.

If you're a vimmer or an emacs user, I can set you up with some basic tooling to make things easier.




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

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

Search: