Ah, so in Python, you have "normal code" then you have AST code. Imagine that they were exactly the same, and whenever you're writing "normal code", you're at the same time writing AST code and vice-versa.
So whenever you want, you can start using "normal code" for manipulating the "normal code" itself, and hopefully now we have yet another perspective on the same thing, for why Lisps are so awesome :)
S-expressions are a nice gimmick, but it’s not the fundamental reason why some Lisps support dynamic patching of code at runtime. Indeed you can easily imagine Lisp written in M-expressions and still support that. Or you can imagine other dynamic languages with reflection gaining this capability with a bit of will, like… check notes… Python.
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
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.
I think there is potential to do something like that with template strings [1] (upcoming feature in Python 3.14). The choice of {} for interpolation isn't ideal because any code with dict and set literals becomes super awkward, but besides that, it could be super interesting for codegen.
I think what I was getting at, is the power this homoiconicity gives you when combined with macros, and I'm not sure how template strings would help with either. Say you want to have a function that receives the code passed into it, instead of whatever the code evaluated to return, could I somehow achieve that with AST and/or template strings in Python?
Say I want `what-code(1 + 1)` to receive `1 + 1` as the argument, not `2`, would either of those things let me do that? At a glance, and without diving deeper, the Ast module could make it so `what-code("1 + 1")` would let the function parse the string and get an Ast, which we could do stuff with, but it pretty much ends at that and is non-ideal for many reasons.
Well... what are you trying to do with "1 + 1"? Lisps are "homoiconic" in the sense that everything is a list, but honestly? When I worked with Scheme in the past, I almost never manipulated code as actual lists. It's not robust. I used match and quote/unquote. What's where I see template strings.
So what you could do is something like this:
addition = "1 + 1"
multiplication = t"{addition} * 7"
result = eval(multiplication) # result is 14
Instead of inserting addition as an exact string, producing "1 + 1 * 7" which is 8, it would parse as an AST and insert the addition node as a unit, so that you get the intended result. You could also do an alpha conversion if the injected code declares variables to avoid name clashes.
It wouldn't let you do true macros, but it would make codegen easier and less error-prone.
> Lisps are "homoiconic" in the sense that everything is a list, but honestly?
That's not how I understand homoiconicity, "everything is a list" or not has nothing to do with it. The point is that whatever structure the compiler uses to understand the code (slightly loose), is the same code you write, since code is represented as data. But if it's maps, lists, booleans or whatever isn't the important takeaway from that.
In your example, how would I, after the multiplication line but before the result line , manipulate "1 + 1" to read something else like "2 + 1"? In Clojure for example, it would be (+ 1 1) so if you wanna change 1 to 2, you use the typical "replace item in list by index" function, but with Python you're either stuck with AST or "code-as-strings". I think that's where a lot of the complexity and error-proneness gets in.
Okay, but even in Lisp, I'm not sure that's a good way to go about it. What do you want to do when you manipulate code, generally? Oftentimes you seek to find all instances of a specific pattern and modify part of that pattern. So why not do something like this:
If you give me a primitive like that in Lisp, I'm going to use it. It's a lot more trustworthy than "replace item in list by index". Because it's nice and all that everything is a list in Lisp, but manipulating code as lists (or maps or whatever) like you describe isn't going to get you very far. If you want to do something as simple as replacing all calls to (f x) by (f x 1), you better hope you don't come across code like (let ((f 123)) ...), for example. Outside of toy cases and examples, you need robust primitives that understand the code's semantic structure.
There's actually a library called ast-grep which does something very similar to what you're describing. They have an example in their introduction which performs a find and replace operation on a JS AST using a pattern:
Is there a good way to verify self-modifying code - in Lisp, or Combo (MOSES), or Python - at runtime against a trusted baseline at loader time?
Dynamic metaprogramming is flexible but dangerous. Python is also "dynamic", meaning that code can be changed at runtime instead of only being able to accidentally pass null function pointers.
Python's metaclasses function similarly to Lisp's macros but in a consistent way: most Python code uses the standard metaclasses so that the macros don't vary from codebase to codebase. The Django "magic removal" story was about eliminating surprise and non-normative metaclassery, for example.
Does this tool monkey patch all copies of a function or just the current reference? There are many existing monkey patching libraries with tests
I get that it works, and it's actually pretty cool that you can do this. But honestly, it feels like it would good, readable code into a tangled mess pretty fast.
> it feels like it would good, readable code into a tangled mess pretty fast
It definitively can, no doubt about it. But used sparingly and only when there is no other way, it can help you remove enormous amount of boilerplate and other things, in a relatively simple, fast and safe way. In my codebases, it does lead to a lot less code, even when most projects just have 2 or 3 macros at most.
Just as one basic example that comes to mind just because I had to do it today: imagine you have a testing suite. When some assertion fails, you'd like to display what value was expected, what value it actually got, and what the exact code was. In JavaScript, I think the most you'd be able to get without involving 3rd party compilers, reading source code from disk or whatnot, would be some functions name (`myfn.toString()`), while in Clojure your macro could capture the entire source code within it, and print it, trivially.
Basically, if you want a function that can take the arguments without evaluating them before executing it, you can do so with macros but without macros you cannot do that. Personally, being able to do so leads to me finding simpler solutions, and expressing them in better ways, compared to if I didn't have them available.
In Python in relation to ast, it does seem so yeah.
If you add two numbers in Python code, it looks like `1 + 1`, but if you use the module from `Lib/ast.py` linked above, how would it look like? I think it would be something like `Expression(body=BinOp(left=Name(id='x',ctx=Load()),op=Add(),right=Name(id='y', ctx=Load())))` which at a glance, certainly looks different than `1 + 1` in my eyes :)
In lisps, `(+ 1 1)` is just `(+ 1 1)` regardless if it's source code or AST, they're just the same thing.
I think you are missing my point. My point is that manually building AST data structures instead of taking them from existing code looks different in Lisp too.
Such a wide net though, what exactly constitutes a "remotely useful project" in your mind? Maybe if we figure out what the exact requirements are, we'll be able to help you with your search :)
> ITA Software by Google
Airfare search engine and airline scheduling software. Cambridge, MA. Common Lisp is used for the core flight search engine. The larger Flights project is roughly equal parts CL, C++, and Java.
Read the last sentence AND this company got acquired by Google like 15 years ago. So ya my question still stands.
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
[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.)
> Ok now do the part where lisp is actually used for any remotely useful project today...
> So ya my question still stands.
That list has 100 companies using lisp today. Were you actually asking if any new companies write in Lisp? Cuz those exist as well - in the same list...
Not surprising! You didn't make any point you just posted a link to a wikipedia page. Again, sources were posted and you cherry-picked one that still confirmed what you asked about. Nw you're doubling down/moving goalposts. Real companies use lisp, in 2025. It's not that big of a deal.
Ah, so in Python, you have "normal code" then you have AST code. Imagine that they were exactly the same, and whenever you're writing "normal code", you're at the same time writing AST code and vice-versa.
So whenever you want, you can start using "normal code" for manipulating the "normal code" itself, and hopefully now we have yet another perspective on the same thing, for why Lisps are so awesome :)
S-expressions are a nice gimmick, but it’s not the fundamental reason why some Lisps support dynamic patching of code at runtime. Indeed you can easily imagine Lisp written in M-expressions and still support that. Or you can imagine other dynamic languages with reflection gaining this capability with a bit of will, like… check notes… Python.
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
to 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.
I think there is potential to do something like that with template strings [1] (upcoming feature in Python 3.14). The choice of {} for interpolation isn't ideal because any code with dict and set literals becomes super awkward, but besides that, it could be super interesting for codegen.
[1] https://peps.python.org/pep-0750/
I think what I was getting at, is the power this homoiconicity gives you when combined with macros, and I'm not sure how template strings would help with either. Say you want to have a function that receives the code passed into it, instead of whatever the code evaluated to return, could I somehow achieve that with AST and/or template strings in Python?
Say I want `what-code(1 + 1)` to receive `1 + 1` as the argument, not `2`, would either of those things let me do that? At a glance, and without diving deeper, the Ast module could make it so `what-code("1 + 1")` would let the function parse the string and get an Ast, which we could do stuff with, but it pretty much ends at that and is non-ideal for many reasons.
Well... what are you trying to do with "1 + 1"? Lisps are "homoiconic" in the sense that everything is a list, but honestly? When I worked with Scheme in the past, I almost never manipulated code as actual lists. It's not robust. I used match and quote/unquote. What's where I see template strings.
So what you could do is something like this:
Instead of inserting addition as an exact string, producing "1 + 1 * 7" which is 8, it would parse as an AST and insert the addition node as a unit, so that you get the intended result. You could also do an alpha conversion if the injected code declares variables to avoid name clashes.It wouldn't let you do true macros, but it would make codegen easier and less error-prone.
> Lisps are "homoiconic" in the sense that everything is a list, but honestly?
That's not how I understand homoiconicity, "everything is a list" or not has nothing to do with it. The point is that whatever structure the compiler uses to understand the code (slightly loose), is the same code you write, since code is represented as data. But if it's maps, lists, booleans or whatever isn't the important takeaway from that.
In your example, how would I, after the multiplication line but before the result line , manipulate "1 + 1" to read something else like "2 + 1"? In Clojure for example, it would be (+ 1 1) so if you wanna change 1 to 2, you use the typical "replace item in list by index" function, but with Python you're either stuck with AST or "code-as-strings". I think that's where a lot of the complexity and error-proneness gets in.
Okay, but even in Lisp, I'm not sure that's a good way to go about it. What do you want to do when you manipulate code, generally? Oftentimes you seek to find all instances of a specific pattern and modify part of that pattern. So why not do something like this:
If you give me a primitive like that in Lisp, I'm going to use it. It's a lot more trustworthy than "replace item in list by index". Because it's nice and all that everything is a list in Lisp, but manipulating code as lists (or maps or whatever) like you describe isn't going to get you very far. If you want to do something as simple as replacing all calls to (f x) by (f x 1), you better hope you don't come across code like (let ((f 123)) ...), for example. Outside of toy cases and examples, you need robust primitives that understand the code's semantic structure.There's actually a library called ast-grep which does something very similar to what you're describing. They have an example in their introduction which performs a find and replace operation on a JS AST using a pattern:
https://ast-grep.github.io/guide/introduction.htmlIs there a good way to verify self-modifying code - in Lisp, or Combo (MOSES), or Python - at runtime against a trusted baseline at loader time?
Dynamic metaprogramming is flexible but dangerous. Python is also "dynamic", meaning that code can be changed at runtime instead of only being able to accidentally pass null function pointers.
Python's metaclasses function similarly to Lisp's macros but in a consistent way: most Python code uses the standard metaclasses so that the macros don't vary from codebase to codebase. The Django "magic removal" story was about eliminating surprise and non-normative metaclassery, for example.
Does this tool monkey patch all copies of a function or just the current reference? There are many existing monkey patching libraries with tests
I get that it works, and it's actually pretty cool that you can do this. But honestly, it feels like it would good, readable code into a tangled mess pretty fast.
> it feels like it would good, readable code into a tangled mess pretty fast
It definitively can, no doubt about it. But used sparingly and only when there is no other way, it can help you remove enormous amount of boilerplate and other things, in a relatively simple, fast and safe way. In my codebases, it does lead to a lot less code, even when most projects just have 2 or 3 macros at most.
Just as one basic example that comes to mind just because I had to do it today: imagine you have a testing suite. When some assertion fails, you'd like to display what value was expected, what value it actually got, and what the exact code was. In JavaScript, I think the most you'd be able to get without involving 3rd party compilers, reading source code from disk or whatnot, would be some functions name (`myfn.toString()`), while in Clojure your macro could capture the entire source code within it, and print it, trivially.
Basically, if you want a function that can take the arguments without evaluating them before executing it, you can do so with macros but without macros you cannot do that. Personally, being able to do so leads to me finding simpler solutions, and expressing them in better ways, compared to if I didn't have them available.
There is not 2 different kinds of code. That's like saying Lisp has normal code and list code.
> There is not 2 different kinds of code
In Python in relation to ast, it does seem so yeah.
If you add two numbers in Python code, it looks like `1 + 1`, but if you use the module from `Lib/ast.py` linked above, how would it look like? I think it would be something like `Expression(body=BinOp(left=Name(id='x',ctx=Load()),op=Add(),right=Name(id='y', ctx=Load())))` which at a glance, certainly looks different than `1 + 1` in my eyes :)
In lisps, `(+ 1 1)` is just `(+ 1 1)` regardless if it's source code or AST, they're just the same thing.
(cons '+ (cons 1 (cons 1 nil))) doesn't look like (+ 1 1).
What about `(+ 1 1) or (quote (+ 1 1)) then?
I think you are missing my point. My point is that manually building AST data structures instead of taking them from existing code looks different in Lisp too.
Ok now do the part where lisp is actually used for any remotely useful project today...
> remotely useful project
Such a wide net though, what exactly constitutes a "remotely useful project" in your mind? Maybe if we figure out what the exact requirements are, we'll be able to help you with your search :)
This site is written in lisp
https://github.com/azzamsa/awesome-lisp-companies
Lololol
> ITA Software by Google Airfare search engine and airline scheduling software. Cambridge, MA. Common Lisp is used for the core flight search engine. The larger Flights project is roughly equal parts CL, C++, and Java.
Read the last sentence AND this company got acquired by Google like 15 years ago. So ya my question still stands.
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
> Ok now do the part where lisp is actually used for any remotely useful project today...
> So ya my question still stands.
That list has 100 companies using lisp today. Were you actually asking if any new companies write in Lisp? Cuz those exist as well - in the same list...
Not sure you know what your own question is!
> That list has 100 companies using lisp today
https://en.m.wikipedia.org/wiki/Wikipedia:Spot_checking_sour...
Ah yes big lisp propaganda out there trying to convince you of the lie
Don't understand what this has to do with my point - do you think you're supposed to only verify "propaganda" sources?
Not surprising! You didn't make any point you just posted a link to a wikipedia page. Again, sources were posted and you cherry-picked one that still confirmed what you asked about. Nw you're doubling down/moving goalposts. Real companies use lisp, in 2025. It's not that big of a deal.
Emacs is one. Naughty Dog used a lisp for Jak and Daxter. Shows up in CAD software.