HNHacker News
TopNewBestAskShowJobs

reikonomusha

10,236 karma · joined February 1, 2010

email: {1}@pm.me where {1} is "lisp".
submissionscomments
reikonomusha··on Not all elementary functions can be expressed with exp-minus-log
"The quintic has no closed form solution" is a theorem that is more precisely stated (in the usual capstone Galois proof) as follows: The quintic has no closed form solution in terms of arbitrary compositions of rational numbers, arithmetic, and Nth roots. We can absolutely express closed form solutions to the quintic if we broaden our repertoire of functions, such as with the Bring radical.

The post's argument is different than the usual Galois theory result about the unsolvability of the quintic, in that it shows a property that must be true about all EML(x,y)-derived functions, and a hypothetical quintic-solver-function does not have that property, so no function we add to our repertoire via EML will solve it (or any other function, elementary or not, that lacks this property).

reikonomusha··on Herbie: Automatically improve imprecise floating point formulas
This is an awesome piece of software, one of my favorite little pieces of magic. Finding more precise or more stable floating point formulas is often arduous and requires a lot of familiarity with the behavior of floats. This finds good formulas completely automatically. Super useful for numerical computation.
reikonomusha··on Dulce et Decorum Est (1921)
Logan Skelton, a music professor at the University of Michigan, really made be find profundity in this poem, not just for its message, but also its construction. I was blown away by his video about it [1]. Really, it was a mind-blown thing for me. I really recommend watching it, it's reasonably short, but the TL;DR is that the meter and choice of silence can be interpreted as meaningfully as the words and context in which it was written.

[1] https://youtu.be/DiCXBn18XYw?t=3m35s

reikonomusha··on Steel Bank Common Lisp
Three (in part or full; presently or recently historically): DWave, Rigetti, HRL
reikonomusha··on Steel Bank Common Lisp
Coalton [1] adds Haskell-style types (so typed lists, type classes, parametric polymorphism, ...) to Common Lisp, and compiles to especially efficient code in SBCL.

[1] https://coalton-lang.github.io/

reikonomusha··on I Wrote a Scheme in 2025
I mean the following with all due respect—and I have a lot of respect for your many efforts and contributions—but it will sound a little blunt, especially as a written comment.

"We have one," no, this is a consistent problem with people who evangelize Lisp. We have had "IDEs" for decades. Most of them, except the couple commercially supported ones, were "experimental", "incomplete", "buggy", etc. This includes the one you link here.

Are these projects valuable as a starting point for other hackers to join in and help? Sure, maybe. Are they helpful for a new programmer? Almost always the answer has been "no". I have first-hand experience subjecting a programmer to one of these tools, and I myself getting incredibly frustrated at how broken it is. Imagine somebody completely new.

You in particular love to advertise these different projects as a form of Lisp evangelism. Advertising the projects is great—I hope they attract helpers—but I think your language around them is deceiving.

> Because yes, before 2023 we could complain there were no JetBrains IDE plugin for Common Lisp, since 2023, we have one.

"We have one" in the absolutely most rudimentary interpretation of that phrase. What we don't have is a working JetBrains Common Lisp IDE suitable for production use.

In order to try to promote a realistic view as to why Lisp doesn't attract more programmers in 2026, I myself will continue to point out Lisp's highly substandard tooling offering until there's an actual product that works. Any Joe can spend a weekend making a 1/2 baked, proof of concept IDE. Even more so now with all the AI vibecoding tools we have at our disposal. It takes much more to make something that checks all the boxes.

reikonomusha··on I Wrote a Scheme in 2025
This is not true.

    (defun f (x)
      (let ((y x))
        (setf y (* y x))
        (block foo
          (if (minusp y)
              (return-from foo y))
          (loop :for i :from 1 :to 10 :do
            ...
This is absolutely typical bog-standard left-to-right top-to-bottom structured programming type code. It also must be executed like so:

  - Define the function 
  - Bind the variable 
  - Mutate the variable
  - Set up a named block
  - Do a conditional return
  - Run a loop
  - ...
The order of execution literally matches the order it's written. But not unlike almost all other languages on the planet, expressions are evaluated inside-out.

Haskell's whole raison d'etre is to allow arbitrary nesting and substitution of terms, and all or none of these terms may or may not be evaluated depending on need. De-nesting happens with a copious number of syntax to bind names to values, sometimes before the expression (via let), sometimes after the expression (via where), and sometimes in the middle of an expression (via do).

reikonomusha··on I Wrote a Scheme in 2025
- Ease of setup and install. Turnkey. Good defaults.

- Non-buffer based workflows.

- Easy access to settings.

- Easy ways to change or switch my compiler.

- Integrated with typical lisp tooling for library, system, and package management. (For example, what Emacs button do I press to set or clear my ASDF compile cache?)

- Better integration of the profiler and debugger. When a Lisp error happens, yet another buffer pops up (breaking the arrangement of all your code windows you set up), this buffer may not even be the only one (but the others are hidden somewhere), and it's not clear what you can even click or expand to see more information (there's a tremendous amount, extremely non-discoverable).

- Good getting started: built in guide for structural editing, REPL workflow, etc.

...and much much more. I say all of this as someone who basically has only used and invested in Emacs for 20 years. I love sharing Emacs with people who like weird technologies and rabbit holes, the real "hacker"-type people. I hate sharing Emacs with people who want to be productive in an hour or so with a Lisp project, because I know within 5 minutes they'll be disappointed, and never get the best of the experience because it's too much uninteresting investment.

I prefer writing Lisp with Emacs+SLIME over anything else. It's extraordinarily powerful, and with enough grit, you can get it to do almost anything you want. But I'm also jealous of people who get to use, say, polished JetBrains products whose goal is to try to give you the best experience possible for your specific programming language.

reikonomusha··on I Wrote a Scheme in 2025
Lisp code is written top to bottom, left to right. Is your grievance more to do with expression-oriented—as opposed to statement-oriented—languages? "Do this" (statement) vs "represent this" (expression)? For instance, do Haskell, OCaml, etc. also irk you in similar ways?
reikonomusha··on I Wrote a Scheme in 2025
Lisp isn't solely defined by DEFMACRO. There are other reasons to use it too, in ways that can complement or compete with DEFMACRO. I also don't really know or understand what the term "flexible" is supposed to mean precisely. What makes a Rust macro less flexible than a Lisp macro in the context of this discussion? In particular, what do Rust macros tamp down on in terms of power that make them more justified for occasional use compared to a Lisp macro?

Lisp has a handful of language features that allow the definition of new syntactic abstractions: form level (DEFMACRO) and character level (SET-MACRO-CHARACTER). Just like it has operators to define new data structures (DEFSTRUCT and DEFCLASS). Just like it had operators to define new functions (DEFUN, DEFGENERIC, DEFMETHOD). Each of these defining forms materially change the way a programmer writes code. You don't need to add new syntax to change the language, and each kind of language change comes with its own tools and practices for working with them, debugging them, documenting them, and so on. All of this is to say: I don't see a good reason to have all of these remarks about syntactic abstraction when they very well could be made about data abstraction or control abstraction.

A useful language feature isn't something that needs to be maximized in idiomatic code. Syntactic abstraction is useful when it's useful, and when it is useful, it's usually extraordinarily useful—typically because a new syntactic abstraction allows a programmer to specify something much more directly or correctly than otherwise. (Other languages frequently resort to external processors or code generators to the same effect. Anybody who has used them knows how frustrating and difficult to debug they can be. But nonetheless, it's not that people do or don't want syntactic abstractions—they clearly do—it's more a matter of how accessible we want to make it to the programmer.)

I did not suggest disallowing it anymore than I suggested we should disallow the definition of new classes in Java. Being judicious and deciding when it's worth it is key, and there are few general statements we can make about this without additional context.

reikonomusha··on I Wrote a Scheme in 2025
This is a common reaction/belief but usually from people who have not actually managed a team of Lisp devs.

Lisp devs are managed in the same way as any other: You have style guidelines, design review, code review, etc. Sometimes a new macro is good and vastly simplifies code. It's accepted as a PR and documented like anything else. Sometimes a new macro is bad, and it's promptly rejected by the team. It's a persistent myth that Lisp programmers are just going to design their own little languages everywhere in a shared code base and it'll be impossible to understand.

(Case in point: Look at open source Lisp code. There isn't even management or code review there! Yet the vast majority of Lisp code is actually just functions and classes, with the occasional macro to reduce boilerplate. In some circumstances, you have a library offering a macro, and it's actually well documented and easy to understand. See Iterate, SERIES, etc. for actual examples.)

Rust or Elixir or Java or whatever aren't at all immune to monstrosities created by astronomically complex or baroque abstractions, FactoryFactoryFactories, and so on. How do teams avoid those? Style guidelines, design review, code review, etc.

reikonomusha··on I Wrote a Scheme in 2025
Three thoughts (in the context of Common Lisp specifically):

- Every day that passes, the gulf between Lisp's tooling and what a typical user expects grows wider. It needs to escape Emacs and SLIME to something that feels complete and polished.

- There needs to be a little bit of a culture shift around Lisp to actually write programs that do things. How many programs can you download via apt or brew that are written in Lisp? They're executables at the end of the day so nothing in principle stops this from happening, but there's just a thread of modern Lisp culture where it's more fun to play around in the REPL and write creative libraries than to ship. (There are notable exceptions of course.)

- I personally like the quirkiness of Common Lisp, but there are so many ways to write it (imperative, functional, etc.), so many ways to structure your programs (one package, package per file, package inferred system, etc.), and so many ways to offer APIs (plain old data and functions, generic function protocols, etc.) that it makes it a combination of confusing and intimidating. I think shifting toward something a little more structured and disciplined like Coalton, while still giving the escape hatches to all of Common Lisp, would help a lot of people "join in" on building new code or building upon existing code.

reikonomusha··on Quirks of Common Lisp Types
The value NIL has type NULL.

No value has type NIL.

reikonomusha··on Quirks of Common Lisp Types
I think this article over-sells (to a point of being misleading) the amount of static type checking offered in Common Lisp even by a relatively good implementation like SBCL.

SBCL's static type checking provides in practice virtually zero guarantees about whether compiling your code actually implies the inexistence of type errors. In that regard, I like to consider SBCL's static type errors to be a mere courtesy [1].

Typically, the static type errors TFA demonstrates are nullified as soon as anything is abstracted away into a function. To show a trivial example:

    (+ 2 "2")
may error statically because the Common Lisp implementation has special rules and knowledge about the + function, but

    (defun id (x) x)

    (+ 2 (id "2"))
will not. Furthermore, Common Lisp has no way to label the type of the function ID to constrain its input type to be the same as its output type that works for all possible types. That is, there's nothing like the Haskell declaration

    id :: a -> a
    id x = x
or the C++

    template <typename A>
    A id(A x) {
        return x;
    }
In Common Lisp, the closest you can get is either to declare ID to be monomorphic (i.e., just select a single type it can work with), as in

    (declaim (ftype (function (string) string) id))
or to say that ID can take anything as input and produce anything (possibly different from the input) as output, as in

    (declaim (ftype (function (t) t) id))
Here, T is not a type variable, but instead the name of the "top" type, the supertype of all types.

[1] SBCL and CMUCL aren't totally random in what static type checking they perform, and the programmer can absolutely make profitable use of this checking, but you have to be quite knowledgeable of the implementation to know what exactly you can rely on.

reikonomusha··on Cognitive load is what matters
I think the criticism is relevant because TFA isn't the first to exercise the term "cognitive load" in the context of computing. It's a term thrown around quite often, so we should cross reference its alleged meaning to literature.

I myself find it to be a term that's effectively used as a thought-terminating cliche, sometimes as a way to defend a critic's preferred coding style and organization.

reikonomusha··on Dynamically patch a Python function's source code at runtime
S-expressions make the user interface to code generation, a component to hot-reloading, more facile. It's not a triviality or gimmick in this context.

We can see from OP that it's actually quite annoying to specify code in a non-S-expression language (or really any language lacking a meta-syntax), usually requiring either

- stuffing and interpolating strings

- building ASTs with API functions provided by the implementation in a tiresome or verbose manner

But you're right that there are more aspects to hot-reloading than just the syntax and data structure.

Common Lisp gets away with it because it actually defines the semantics of redefinition of functions and classes. For instance, the standard says what will happen to all existing FOO object instances in memory if I change

    (defclass foo ()
      ((a :initarg :a)))
to

    (defclass foo ()
      ((a :initarg :a)
       (b :initarg :b)))
and even lets the programmer customize the behavior of such a redefinition.

Few if any languages go through the trouble of actually defining these semantics, especially in the context of a compiled language (like Common Lisp), making all but the simplest instances of reloading code well defined.

reikonomusha··on Dynamically patch a Python function's source code at runtime
You asked where Lisp is useful, and I supplied a list of companies that find (or, in some cases of recent history, found) Lisp useful. Your Google example is pertinent, because Google had the resources to wholesale eliminate its use of Lisp any time within the last 15 years, but for some reason worth pondering, hasn't. Instead, they continue to develop the product in Lisp, and continue to contribute to the Common Lisp open-source ecosystem.

But that aside, if you want a fresh look at what people are thinking about with Lisp, maybe check out the talks that were given this year at the 2025 European Lisp Symposium [1,2]. Or perhaps look at how someone shipped a platformer game on Steam with Common Lisp [3,4], and is in the finishing lap porting it to the Nintendo Switch [5].

I realize, though, that this kind of "debate" (?) is never satisfying to the instigator. If it does satisfy though, I will agree with you that—despite all of the claims of alleged productivity and power the language offers—Common Lisp remains far less popular than Python, which I assume is your only real point here.

[1] A presentation about how adding a static type system to Common Lisp à la Haskell helps write mission critical programs in defense and quantum computing: https://youtu.be/of92m4XNgrM

[2] A talk from employees of Keepit, a company that supplies a SaaS backup service, discussed how they train people on Common Lisp when employing them: https://youtu.be/UCxy1tvsjMs?t=66m51s

[3] Discusses technical details of how Lisp was used to implement a game that was actually shipped: https://reader.tymoon.eu/article/413

[4] The actual game that you can buy: https://store.steampowered.com/app/1261430/Kandria/ (This is not intended to be an advertisement and I'm unaffiliated. It's just a demonstration of a recently "shipped" product written in Common Lisp where you might not expect it.)

[5] Technical discussion of the Nintendo Switch port: https://youtu.be/kiMmo0yWGKI?t=113m20s

reikonomusha··on Dynamically patch a Python function's source code at runtime
https://github.com/azzamsa/awesome-lisp-companies
reikonomusha··on Seed: Interactive software environment based on Common Lisp
CLOG [1] seems to do something similar (though it's hard to tell; Seed's README isn't terribly informative), except CLOG has more tutorials, is better documented, has a more fleshed out README, and has ongoing support.

[1] https://github.com/rabbibotton/clog

reikonomusha··on An interactive guide to sensor fusion with quaternions
It doesn't make sense in and of itself. We usually think of division as a closed operation: we divide two things (like real numbers) and we get the same kind of thing out (another real number).

In Hamilton's original view of quaternions, he defined a "geometric quotient" of two 3d directed lines (one kind of object) as being a quaternion (another kind of object), and gave all sorts of complicated geometric formulas for how to calculate it.

reikonomusha··on Coalton Playground: Type-Safe Lisp in the Browser
Since a macro is a definition of syntax, I think you'd essentially need something like typing judgments to show how the typed elements of the syntax relate to one another, so that the type checker (e.g., a typical Hindley-Milner unifier) can use those rules. These are usually written as the fraction-looking things that show up in PLT papers. This is, as GP says, essentially extending the type system, which is a task fraught with peril (people write entire papers about type system extensions and their soundness, confluence, etc.).
reikonomusha··on Coalton Playground: Type-Safe Lisp in the Browser
I think you're right that debugging errors involving complicated macros can get difficult, but to at least make the situation more tolerable, when Coalton expands a macro, it remembers where the expansion came from, so an error will be reported in the right place with the right source code. For example, using the RPN macro from the sister comment, here's an intentional type error:

    COALTON-USER> (coalton (rpn "x" "y" +)) 
      --> <macroexpansion>:1:9
       |
     1 |  (COALTON (RPN "x" "y" +))
       |           ^^^^^^^^^^^^^^^ expression has type ∀. (NUM STRING) => STRING with unresolved constraint (NUM STRING)
       |           ^^^^^^^^^^^^^^^ Add a type assertion with THE to resolve ambiguity
       [Condition of type COALTON-IMPL/TYPECHECKER/BASE:TC-ERROR]
reikonomusha··on Coalton Playground: Type-Safe Lisp in the Browser
Then nothing. Macros work.

Silly example:

    COALTON-USER> (defmacro rpn (x y op)
                    `(,op ,x ,y))
    RPN

    COALTON-USER> (coalton-toplevel
                    (define (double x)
                      (rpn 2 x *)))
    ;; DOUBLE :: ∀ A. NUM A ⇒ (A → A)

    COALTON-USER> (coalton (double 3.0))
    6.0
reikonomusha··on Coalton Playground: Type-Safe Lisp in the Browser
It can detect simple-ish instances, like calling

    (fn-t "hello" "world")
But a good rule-of-thumb is that these compile-time type errors are more of a courtesy, rather than a guarantee. As soon as you abstract over fn-t with another function, like so:

    (defun g (x y)
      (fn-t x y))
and proceed to use g in your code, all the static checking won't happen anymore, because as far as g is concerned, it can take any input argument types.

    CL-USER> (defun will-it-type-error? ()
               (g "x" "y"))
    ;; compilation WILL-IT-TYPE-ERROR? successful
No compile-time warning is issued. Contrast with Coalton:

    COALTON-USER> (coalton-toplevel
                    (declare fn-t (U8 -> U8 -> U8))
                    (define (fn-t x y)
                      (* x y))
                    
                    (define (g x y)
                      (fn-t x y))
                    
                    (define (will-it-type-error?)
                      (g "hello" "world")))

    error: Type mismatch
      --> <macroexpansion>:8:7
       |
     8 |      (G "hello" "world")))
       |         ^^^^^^^ Expected type 'U8' but got 'STRING'
       [Condition of type COALTON-IMPL/TYPECHECKER/BASE:TC-ERROR]
reikonomusha··on Coalton Playground: Type-Safe Lisp in the Browser
I think it's three things:

1. Bringing abstractions that are only possible with static types, like ad hoc polymorphism via type classes. For example, type classes allow polymorphism on the return type rather than the argument types. Something like

    (declare stringify (Into :a String => :a -> :a -> String))
    (define (stringify a b)
      (str:concat (into a) (into b)))

    ; COALTON-USER> (coalton (stringify 1 2))
    ; "12"
The function `into` is not possible in a typical dynamically typed language, at least if we aim for the language to be efficient. It only takes one argument, but what it does depends on what it's expected to return. Here, it's expected to return a string, so it knows to convert the argument type to a string (should knowledge of how to do that be known by the compiler). Common Lisp's closest equivalents would be

    (concatenate 'string (coerce a 'string) (coerce b 'string))
which, incidentally, won't actually do what we want.

2. Making high performance more accessible. It's possible to get very high performance out of Common Lisp, but it usually leads to creating difficult or inextensible abstractions. A lot of very high performance Common Lisp code ends up effectively looking like monomorphic imperative code; it's the most practical way to coax the compiler into producing efficient assembly.

Coalton, though, has an optimizing compiler that does (some amount of) heuristic inlining, representation selection, stack allocation, constant folding, call-site optimization, code motion, etc. Common Lisp often can't do certain optimizations because the language must respect the standard, which allows things to be redefined at run-time, for example. Coalton's delineation of "development" and "release" modes gives the programmer the option to say "I'm done!" and let the compiler rip through the code and optimize it.

3. Type safety, of course, in the spirit of ML/Haskell/etc.

reikonomusha··on Coalton Playground: Type-Safe Lisp in the Browser
As far as your first question is concerned, macros in and of themselves are not incompatible with a typed language. Coalton uses Lisp macros, for example, and they work seamlessly and as expected.

But a Coalton macro is typically written in Lisp, not Coalton. This isn't in and of itself a problem---it's very easy and straightforward thus to write Common Lisp-style macros in Coalton. The difficulties arise when the macro must itself be (1) written in a statically typed manner, which gets into having a complete typing on your metalanguage (or your AST), and (2) allowed to access the type environment of the surrounding context in which the expansion is happening. If (2) should be accomplished, then the type-checking machinery must collaborate with the macro-expansion machinery, and that in practice makes both tasks very difficult to specify semantics for and implement.

The language Hackett [1] worked toward solving the problem of having true typed and type-aware macros (they call them "type-aware" and "type-directed" macros [2]). Development unfortunately ceased ~7 years ago.

[1] https://github.com/lexi-lambda/hackett

[2] I think this video has a good discussion of Lisp, macros, and static types, from the perspective of implementing a Haskell in Racket. https://www.youtube.com/watch?v=5QQdI3P7MdY

reikonomusha··on Hacker News now runs on top of Common Lisp
In addition, Common Lisp provides standardized ways to get fast code: OPTIMIZE policy, type annotations, stack allocations, disassemblies, etc. This is all there before you get to SBCL's specific tools for optimization and profiling.
reikonomusha··on You can choose tools that make you happy
This is a disappointing article from an author who usually writes excellent works. The article touches on a seedling of truth, but then takes it in a misleading direction. Unfortunately, I don't think this article actually inspires anybody to inquire more carefully or rationally within, but instead provides great thought-terminating ammunition that can be fired at anybody doing something not perceived as popular, ordinary, or boring. (And it's entirely in vogue to do so, with all the Grug Programmer, Use Boring Technology, etc. haranguing these days.)

What I think is true: A person will often hide their true rationale and/or intent of a decision behind a facade of weak, ex post facto objective arguments.

I think the author is absolutely right that it's OK to use something just because it makes you happy. Especially for personal/hobby projects, you don't need to be disingenuous and attempt convince the world that SWI-Prolog was truly the optimal technical choice to build your CRUD web app. Saying "I like Prolog and I built a CRUD web app using it" is perfectly fine. I'm really on board with this intellectual honesty.

Where the author goes off the rails is how he ties this criticism specifically to people who make "obscure" decisions. He does so especially through alienating caricature. The subtext of the article is that if you're not doing something conventional, then avoid being a sophist from the get-go and just say how you really feel: it makes you happy. He doesn't lend any credence whatsoever to the idea that an obscure choice may actually be technically justified.

The reality is that people who choose popular technologies often do so without any good reason either, and they too provide a facade of objective justification. They may not be writing blog posts that offend the author ("Why I Chose Python to Build XYZ") that reach the top of Hacker News, but they are espousing their own weak, ex post facto justifications in settings when their decision is challenged—which is almost inevitable, as their choice of popular technology isn't the only popular technology. They'll manufacture the same load of baloney for their decision in the same way the author's Common Lisper boogeyman does.

I would even make a stronger claim: People decide to use popular technologies because it makes them happy, and this happens far more often in load-bearing situations than the Obscure Weekend Technologists do. Furthermore, popularity serves as a sort of immunity to further technical inquiry.

This has led to far more technical derangement and damage writ large to the field than the weekend APLer writing about why they chose APL for a project, and I think it demands scrutiny the author doesn't offer in the slightest, despite being entirely relevant to his broader criticism.

More subjectively, after reading the article, I just felt the author is sour about something or has some sort of ax to grind. He's no stranger to obscure technologies (ex-Common Lisper; now disavows it) and has even created his own obscure programming language with an obscure Wirthian syntax written in an obscure OCaml. Maybe this article represents an admission of sorts.

reikonomusha··on The Lisp in the Cellar: Dependent types that live upstairs [pdf]
Related context: The 2025 European Lisp Symposium [1] was just wrapped a few hours ago in Zurich. There was content on:

- Static typing a la Haskell with Coalton in Common Lisp

- Dependent typing with Deputy in Clojure (this post)

- The Common Lisp compiler SBCL ported to the Nintendo Switch

- Common Lisp and AI/deep learning

- A special retrospective on Modula and Oberon

- Many lightning talks.

[1] https://european-lisp-symposium.org/2025/index.html

reikonomusha··on Using Coalton to implement a quantum compiler (2022)
Coalton can be used wherever (almost any) Common Lisp can be used: mixed in, side by side, exclusively, as an library, etc.

CIEL doesn't presently ship any native Coalton interfaces, so all invocations of CIEL within Coalton code would have to be in a "lisp" form, which is like Rust's "unsafe".

    (define (some-coalton-function arg)
      ;; break out to Lisp
      (lisp String (arg)
        ...CIEL stuff here...))
On ordinary safety settings, the String declaration on the Lisp code will be checked at runtime so that wrong types don't leak back into the surrounding/calling Coalton code.

Conversely, Coalton code can be freely used within Common Lisp code regardless of whether it uses CIEL.

← PreviousPage 2 of 34Next →