Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Having programmed professionally in 36 languages, I must agree with this. I am having so much fun with Lisp, that I am not likely to go back.

> Often developers say how you can make a mess of a Common Lisp codebase because of such freedom it provides. But isn’t that the same with any language?

There is a story about the code behind the terminal interface for Tops-10 for the PDO-10. It had been tweaked over the years adding various features, such as using control-T to see what was happening in the running program. Many attempts were made to rewrite it, but it was so intertwined, that whoever tried fixing it gave up.

Three things are killer features for me with the slime+emacs running sbcl. First is that you can see the underlying source for anything down to the lisp implementation, e.g. the definition of 'macrolet'.

The second thing is if there is an error, it displays the stack with the ability to see what the local variables are in any function on the stack.

The third thing is that one can patch a running program, either once a breakpoint ('break) is encountered, or by doing control-C at the repl. And then resume it.

It is also fun that SBCL is unreasonably fast. It compiles itself in 2m15s. I am pretty sure that gcc/clang/llvm take longer than that and also likely Rust.

I have developed a habit of using s-expresions for lots of things, such as configuration files, and web page representations. I blame paredit and lisp for this tendency. See https://github.com/wglb/configuration-r



I wish you could use Genera. If you like emacs/slime. You would see how much we lost :(


But here's the thing, and I don't know, I never worked with Genera.

But.

Genera has been gone for over 30 years. At the time, it was certainly revolutionary. But in the computing world, where things move VERY fast, whatever made Genera amazing clearly wasn't amazing enough to replicate in full anywhere else.

I'm not even talking about other languages, trying to get that dynamic environment in modern languages, with modern tooling (which has made enormous strides over the years). But not even in the Lisp world has it resurfaced. Nobody is talking about how amazing Franz Lisp is, or LispWorks, both long running, successful Common Lisp implementations. Run by smart people, run by people "who were there".

We have Slime, with Emacs. We've had Slime forever. We've had solid Common Lisp implementations...forever! People still lament how good the Macintosh Lisp environment was (and honestly, I don't know how, or what made it more than what Slime might offer beyond nice access to the Mac toolbox).

Do you know what has changed in modern Common Lisp implementations since Genera? NOTHING! It's Common Lisp! Common Lisp is 30 years old, and a superset of whatever Lisp Genera was running. And you can't tell me that we don't have modern Genera because Flavors (Genera's object system) is so amazing and CLOS is lacking (because even if it was, it's Common Lisp -- you can FIX IT!, or simply implement Flavors because it's written in LISP!).

There are no secrets here.

Veterans speak of Genera in hushed tones. Sing songs about it. But, however good it was, it's, demonstrably, not good enough to do it again.

Meanwhile, there's been a dedicated pack of folks over at the Pharo side making Smalltalk even more Smalltalk with lots of new development tooling. These are folks that write Pharo so that they can make writing more Pharo even better. I swear, I think they're to the point that they just mount GIT as a file system now over there. I'm not a Smalltalk guy, but, boy, those folks have been riding a rocket for years.

So, it's not lost. It's abandoned. Left behind. For whatever reason. There's a difference.


Another thing is, that despite Genera having been gone from the real world for over 30 years, it is still nonfree, and most of the interesting software that ran on it hasn't been publically archived either. That has made it essentially worthless to most people who would otherwise be interested in it. On the contrary, the MIT Lisp Machine system from which Genera was derived is completely free, as well as the LMI system which was another derivative. And there are a small handful of enthusiasts who continue to use, share and improve them.


Not Genera, but InterLisp/Medley it's MIT licensed.


Yes, and the Medley Interlisp revival project seems to have become very successful.


> Another thing is, that despite Genera having been gone from the real world for over 30 years, it is still nonfree

Eh. That's a non-starter. The code may be closed, the concepts aren't. We've been reinventing the wheel for ages, and the Genera developer experience apparently isn't novel enough to be worthy of being reinvented.


My point is that for people who wish to experience a Lisp Machine system for what it is, and explore its unique concepts, they have free systems they could be looking to rather than a dead proprietary one.


> I don't know how, or what made it more than what Slime might offer beyond nice access to the Mac toolbox).

GNU Emacs plus SLIME is a remote development environment for an external Common Lisp, using a text-editor-based environment.

Macintosh Common Lisp was an integrated GUI-based Common Lisp develop environment written in itself.

Those are two very different things.

> Flavors (Genera's object system)

CLOS and Flavors are both Genera's object systems. CLOS was actually co-developed by Symbolics. The core designers were Keene & Moon from Symbolics Inc., Kiczales & Bobrow from Xerox PARC. and DeMichiel & Gabriel from Lucid Inc.


What Genera ideas are Emacs/SLIME missing out on that would improve them?


A microcode tweaked down for Lisp. The whole OS being a Lisp image.

Imagine running Common Lisp in bare metal. Like Mezzano OS does, but with your Intel malware ^U CPU firmware designed to run CL at even faster speeds.


YouTube video demoing Genera, among other things:

https://www.youtube.com/watch?v=7RNbIEJvjUA


I've heard stories about that. Sounds awesome.


> It compiles itself in 2m15s.

My compile goes in about 1m18s. I do have it set to use multiple threads/cores.


> Three things are killer features for me with the slime+emacs running sbcl. First is that you can see the underlying source for anything down to the lisp implementation, e.g. the definition of 'macrolet'.

If you’re working on a project (on its code), then you can (obviously) see its code as well.

Statically typed languages make exploring the code a lot easier, especially with an IDE which leverages the type information to assist in code exploration.

> The second thing is if there is an error, it displays the stack with the ability to see what the local variables are in any function on the stack.

This works with debuggers (and is easy with an IDE) in most popular languages.

> The third thing is that one can patch a running program, either once a breakpoint ('break) is encountered, or by doing control-C at the repl. And then resume it.

You can do this with most major languages today as well.


Common Lisp also provides type information, dynamic type information.

    CL-USER 72 > (defclass person () (name age))
    #<STANDARD-CLASS PERSON 80101598FB>
I can now ask Lisp to find the class object named person.

    CL-USER 73 > (find-class 'person)
    #<STANDARD-CLASS PERSON 80101598FB>
Now I've got a real class object, not just a pointer to dead code. I can inspect it, change it...

I'm adding a slot to an existing class:

    CL-USER 74 > (defclass person () (name age height))
    #<STANDARD-CLASS PERSON 80101598FB>
I can then ask for the slots of that class.

    CL-USER 75 > (class-direct-slots (find-class 'person))
    (#<STANDARD-DIRECT-SLOT-DEFINITION NAME 8220278CA3>
     #<STANDARD-DIRECT-SLOT-DEFINITION AGE 8220278CBB>
     #<STANDARD-DIRECT-SLOT-DEFINITION HEIGHT 8220278CD3>)
I can define a method for it.

    CL-USER 76 > (defmethod greet ((a-person person)) (format t "Hello ~a" (slot-value a-person 'name)))
    #<STANDARD-METHOD GREET NIL (PERSON) 801001B69B>
I now can retrieve the method object from its generic function.

    CL-USER 77 > (find-method #'greet () (list (find-class 'person)))
    #<STANDARD-METHOD GREET NIL (PERSON) 801001B69B>
That's live code exploration using dynamic types.

> You can do this with most major languages today as well.

Hmm, I'm in this graphics program on my Mac, how do I "break" the program and then interact with its Objective-C objects?


I was likely not clear in my explanation.

These three things are all done from the REPL. I don't have any experience doing C or C++ from a REPL.

I disagree that "Statically typed languages make exploring the code a lot easier." I'm not aware of an equivalent in C or C++ to the "esc-." method of going directly to the source.

The keystroke combination to find the definition of a function or symbol using slime and emacs is "esc-." That takes you right to the code. If it is a symbol defined by Lisp, you see the underlying code in the actual source for Lisp.

So the second thing is also done when running a program from the REPL. Once tripped, you can expand each element in the stack to see and inspect the variables local to that function. You can then edit the source for that function, compile only that function in place, then resume execution. I admin that I don't know how to do that in C or C++.

My explanation should have referenced the REPL, which is key to these.


> Once tripped, you can expand each element in the stack to see and inspect the variables local to that function.

When a breakpoint is tripped, with most modern IDEs and languages, you can see the local stack, global state (and pretty much all state), and this state is presented in a UI with dropdowns, etc. Debuggers in modern IDEs also let you evaluate an expression in place, where the breakpoint was tripped, so you can further dig into the local state with a debugger. This sort of works like a REPL (but you're "sort of" limited to a single expression -- but that's something you can still circumvent with an immediately-called lambda).

>You can then edit the source for that function, compile only that function in place, then resume execution.

This is supported by some languages, but admittedly support for this varies widely, and sometimes can be weak or finicky. For example, the JVM family of languages have a feature called HotSwap. This feature is quite old: https://www.jrebel.com/blog/java-hotswap-guide -- it was introduced in 2002 with the Java 1.4 JVM. I use it even today, with personal projects, where I update the code of a running server live (without restarting the server).

HotSwap unfortunately sometimes is broken with certain major JVM frameworks, but for my personal projects, I've always made sure to only use libraries that work well with HotSwap, since I consider live code patching of a running server to be an absolutely important feature.

>the "esc-." method of going directly to the source

To be honest, I'm not entirely sure what you mean here. When I'm working on a project (as in, I have an IDE open for it, and I'm writing the code for it), all the code is right there. When I use a debugger, I can see the stack of all the ancestral functions, and with a single click I can go to the definition of any function. Static typing basically makes the "Go to definition" more precise, as it always jumps to the place in the code (file & line) where any particular function or value is defined. With dynamically typed languages, an IDE can sometimes show you multiple option when there's multiple functions with the same name defined in different places.

>These three things are all done from the REPL. I don't have any experience doing C or C++ from a REPL.

Most of the features I've described above work with C and C++ IDEs (like CLion, Visual Studio, etc), except for the live code hot swapping -- I've only used live code patching with JVM HotSwap with Java, Kotlin, etc.


> Most of the features I've described above work with C and C++ IDEs

In Common Lisp you won't need an IDE and you won't need to instrument the application with a debugger, because much of the features are built into the language runtime.

For example Common Lisp has an error handling system with resumeable exceptions.

Another example is the object system where the objects are updated on changes. For example I can load a new class description and the existing objects are changed to the new description: new slots, fewer slots, new superclass, removed superclass, ... This makes hot loading much more useful. Additional many calls to global functions are late bound, which again makes hot loading more useful.

A typical runtime also includes a code loader, a compiler and/or an interpreter, which makes updating code easy. All without an IDE or a foreign debugger.

This also means that when for example the resident compiler is called, the compiler is a part of the runtime. An error under compilation, will show me the compiler in the stack trace and the compiler itself can be live edited/updated, while I'm in a compilation error.

The effect is then, that by default programs are running inside a runtime, which always provides the features of interpretation, compilation, code loading, repls, debug repls, runtime type checks, OOP updates, ... One would need to do something to turn those things off.

In a typical C/C++ program, the compiler, debugger, IDE, linker, ... are not a part of the program's runtime.


All of this late binding, and needing a runtime, seems to suggest even a JIT LISP implementation (e.g. SBCL) might not be able to achieve performance similar to a "zero-cost abstraction" language like Rust. When you're in debug mode (e.g. with C++ or Rust or a JVM language), there's a bunch of debug info added to the binary plus various optimizations are turned off. I'm sure some optimizations that C++ / Rust / LLVM use involve entire eliding classes and other abstractions, so that an "O3" release-mode binary doesn't even contain any cruft. There's an argument to be made for this approach, at least for software that's run on customers' computer. For server side software, I can see why having always-available debuggability is useful.


> might not be able to achieve performance similar to a "zero-cost abstraction" language like Rust

That might be the case, even though SBCL might have some performance improving features, like a runtime compiler (which can be used to improve performance at runtime) and the capability to implement assembler extensions to the compiler from Lisp.

> There's an argument to be made for this approach, at least for software that's run on customers' computer. For server side software, I can see why having always-available debuggability is useful.

I see no reason, why this should not be useful on the client side, too. Many software systems on the client side have ways to extend them. Doing this in the main implementation language can have advantages, instead of doing it in a limited macro or scripting language.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

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

Search: