Programming is in tension between the Light Side and the Dark Side.
The Light Side is about preventing the programmer from making mistakes: Get rid of go-tos! Add static types! Do not allow a bug to be expressible. The Dark Side is about giving power to the programmer: Macros? Obviously. Operator overloading? Self-modifying code? Multi-line reg-exps? Go to town!
The Light Side knows programmers are flawed and imposes constraints. The Dark Side trusts programmers with power. Neither side is correct all of the time, and a good programmer learns both.
Lisp is interesting in that it is clearly Dark Side programming (the programmer can do anything) but it's still admired by Light Side programmers. Maybe there's something about the simplicity of the language that makes it seem platonic--almost incorruptible. Or maybe Lisp is so pure that it embodies both Light Side and Dark Side, like a god that spawned the programming universe.
The website seems to have a bug with syntax highlighting. Pieces of code included in the post text are black, you can still see the actual text if you select it with your mouse. Same bug on Chrome desktop and on Safari on iPad
There are some truly powerful and unique things about Lisps, but I wish articles like this would stop including REPLs and hot-reloading. The former have been table stakes for interpreted languages (and some compiled ones!) for years, and the latter is neither unique nor particularly widely used (hot reloads have to tangle with state and patching, so resetting the world for ease of reasoning is considered a best practice for a reason).
> The former have been table stakes for interpreted languages
I used to think so too back in the day when I was getting into Clojure. It was much later when I realized that when Lisp people talk about the REPL they’re usually talking not so much about the interactive CLI where you can evaluate commands easily but more so the ability to connect your program to a live session where you can quickly evaluate forms within your text editor and in the context of your running application, which enables much more interactive development than in other interpreted languages.
I do think it is worth highlighting how many advanced parts of hot-reloading have already been covered in Common Lisp. Same with highlighting how the REPL is largely not used to directly type into, but is instead a very powerful interface for tools to interact with a running image.
But, again agreed that simply these existing are not that notable today.
I must admit - I still don't understand macros. I get that they're code that's generated at compile time. But I don't understand how that's different than a function which evaluates other functions. I guess the latter would actually be evaluated at runtime? I think I get it conceptually but I'm not sure I have the muscle memory to reach for them. Anybody here have an "ah hah!" Moment with macros?
Sure, macros are functions that take functions as input, and produce new functions as output. But they take the function's symbols as input and produce a new set of symbols. So a macro can extend the syntax of the language without having to modify the core language system. Anyway, what's unique about Lisp macros vs say, Rust macros, or C style preprocessors, is "homoiconicity". The data structure that a Lisp macro takes as an input, the lisp code, is the same data structure that the language uses normally (S-expressions, lists...), so writing a macro requires few new language skills compared to writing normal lisp (again, compare writing Rust macros, a dark art in comparison).
If you haven't read it, I'd suggest taking a look at Paul Graham's book On Lisp [0]. He says better, and with more examples than I'd provide in a comment block, what I'd write on the subject. Jump to chapter 8 for his discussion on the topic, referring back to chapter 7 if you find the macro definitions difficult to read.
If it helps, https://taeric.github.io/CodeAsData.html was my attempt at exploring "code as data" and what makes lisp different here. My specific focus was more to point out that "eval" in lisp doesn't just take in a string. But I think the same general points remain.
1. Come up with an algorithm to define an algorithm.
2. Code expansion. Instead of typing out 1000 classes that are best represented as a template of a single class, you can define a macro then use it.
3. C++ at least uses them to provide generics.
4. They let you peel back the layers of abstraction to use the language itself as an API. Useful if you want to write static analysis to do analysis on code quality, security, linting, etc.
5. Anything you can imagine, it's metaprogramming.
There's a bit of a mental model flip to make maybe.
> they're code that's generated at compile time
They're code that generates code at compile time. Macros can actively walk the AST of the parameters they process and rewrite them completely into new shapes. That transformed AST is what then actually gets compiled.
one way to see it is that it's a function that runs at compile time. for instance instead of dumping magic numbers/tables in a codebase you could put the code and substitute to their value at compile time.
but also it can change your code, so you get to do all the java annotation magic stuff.
23 comments:
I've been wondering - Is lisp (common lisp, clojure, scheme) easier for iterative work with LLMs?
Programming is in tension between the Light Side and the Dark Side.
The Light Side is about preventing the programmer from making mistakes: Get rid of go-tos! Add static types! Do not allow a bug to be expressible. The Dark Side is about giving power to the programmer: Macros? Obviously. Operator overloading? Self-modifying code? Multi-line reg-exps? Go to town!
The Light Side knows programmers are flawed and imposes constraints. The Dark Side trusts programmers with power. Neither side is correct all of the time, and a good programmer learns both.
Lisp is interesting in that it is clearly Dark Side programming (the programmer can do anything) but it's still admired by Light Side programmers. Maybe there's something about the simplicity of the language that makes it seem platonic--almost incorruptible. Or maybe Lisp is so pure that it embodies both Light Side and Dark Side, like a god that spawned the programming universe.
Kind of agree, but in my view preventing the programmer to make mistakes is futile. I have seen awful stuff in languages made to prevent errors.
It's much better to give all the power to the programmer, to allow him to fix his mistakes rather than fantasising about preventing them.
The website seems to have a bug with syntax highlighting. Pieces of code included in the post text are black, you can still see the actual text if you select it with your mouse. Same bug on Chrome desktop and on Safari on iPad
I get this on both FF and Chrome.
My first thought was that the article was redacted lol
metoo
There are some truly powerful and unique things about Lisps, but I wish articles like this would stop including REPLs and hot-reloading. The former have been table stakes for interpreted languages (and some compiled ones!) for years, and the latter is neither unique nor particularly widely used (hot reloads have to tangle with state and patching, so resetting the world for ease of reasoning is considered a best practice for a reason).
> The former have been table stakes for interpreted languages
I used to think so too back in the day when I was getting into Clojure. It was much later when I realized that when Lisp people talk about the REPL they’re usually talking not so much about the interactive CLI where you can evaluate commands easily but more so the ability to connect your program to a live session where you can quickly evaluate forms within your text editor and in the context of your running application, which enables much more interactive development than in other interpreted languages.
You've been able to do this in Ruby since I can remember. Not a lot of editors take advantage of it though...
Largely agreed.
I do think it is worth highlighting how many advanced parts of hot-reloading have already been covered in Common Lisp. Same with highlighting how the REPL is largely not used to directly type into, but is instead a very powerful interface for tools to interact with a running image.
But, again agreed that simply these existing are not that notable today.
But with Clojure and immutable by default, hot reload is a real thing, it sounds like not just on JVM but even among lisps.
I thought this was going to be https://web.archive.org/web/20120106121645/http://wiki.alu.o.... Brings back memories of following comp.lang.lisp, not least as a sort of soap opera. Lots of characters and drama, as well as programming wizardry.
All roads lead to Lisp
What has Lisp ever done for us?
The aqueducts!
I must admit - I still don't understand macros. I get that they're code that's generated at compile time. But I don't understand how that's different than a function which evaluates other functions. I guess the latter would actually be evaluated at runtime? I think I get it conceptually but I'm not sure I have the muscle memory to reach for them. Anybody here have an "ah hah!" Moment with macros?
Sure, macros are functions that take functions as input, and produce new functions as output. But they take the function's symbols as input and produce a new set of symbols. So a macro can extend the syntax of the language without having to modify the core language system. Anyway, what's unique about Lisp macros vs say, Rust macros, or C style preprocessors, is "homoiconicity". The data structure that a Lisp macro takes as an input, the lisp code, is the same data structure that the language uses normally (S-expressions, lists...), so writing a macro requires few new language skills compared to writing normal lisp (again, compare writing Rust macros, a dark art in comparison).
If you haven't read it, I'd suggest taking a look at Paul Graham's book On Lisp [0]. He says better, and with more examples than I'd provide in a comment block, what I'd write on the subject. Jump to chapter 8 for his discussion on the topic, referring back to chapter 7 if you find the macro definitions difficult to read.
[0] https://www.paulgraham.com/onlisptext.html
If it helps, https://taeric.github.io/CodeAsData.html was my attempt at exploring "code as data" and what makes lisp different here. My specific focus was more to point out that "eval" in lisp doesn't just take in a string. But I think the same general points remain.
You could use them to:
1. Come up with an algorithm to define an algorithm.
2. Code expansion. Instead of typing out 1000 classes that are best represented as a template of a single class, you can define a macro then use it.
3. C++ at least uses them to provide generics.
4. They let you peel back the layers of abstraction to use the language itself as an API. Useful if you want to write static analysis to do analysis on code quality, security, linting, etc.
5. Anything you can imagine, it's metaprogramming.
There's a bit of a mental model flip to make maybe.
> they're code that's generated at compile time
They're code that generates code at compile time. Macros can actively walk the AST of the parameters they process and rewrite them completely into new shapes. That transformed AST is what then actually gets compiled.
one way to see it is that it's a function that runs at compile time. for instance instead of dumping magic numbers/tables in a codebase you could put the code and substitute to their value at compile time.
but also it can change your code, so you get to do all the java annotation magic stuff.