Live data from Hacker News

Understanding the Power of Lisp (2020)

joshbradley.me

111–120 of 140 posts

Re: Understanding the Power of Lisp (2020)

#111
post #76

Earlier quoted context omitted.

A canonical example of macro capabilities that methods/functions cannot recreate is a short-circuiting conditional. Clojure's only built-in conditional operator is 'if'. Despite this, we have nice short-circuiting or, and, when, unless, and other conditional operations in Clojure, defined as macros. Clojure (and C# and most languages) eagerly evaluate their function arguments. You cannot write a function that short c…

Thank you for the examples. I'm really curious to get to the bottom of this. My inspiration has been Erik Meijer, specifically his article "Confessions of a Used Programming Language Salesman. Getting the Masses Hooked on Haskell" [1]. If I understand you correctly, you are implementing lazy evaluation in your when macro, something you call short-circuiting. By dropping laziness in strict functional languages such as…

I'm happy to chat more if it's helpful (contact details in profile). I'll be honest that I didn't get Lisp for a long time, and stilll wouldn't claim true expertise. I attempted SICP a few times based on the glowing reviews from folks I admire. One of my paths to tech was via Paul Graham's writings, so I had a bias for Lisp early on.

Two parts here. First, and briefly a note on short circuiting and lazy evaluation. Second, and longer, a further discussion of Lisp macros.

First. Short circuiting is a special case of lazy evaluation, but is the most common exposure to lazy evaluation among programmers in the C family of languages. It is the property of conditional and boolean operations that evaluate only the minimum necessary to return a result. (or true IGNORED) only needs to evaluate the first operand to or, true, because the result of the or is true regardless of the value of IGNORED. This is common to all mainstream strictly evaluated languages I am aware of. https://en.wikipedia.org/wiki/Short-circuit_evaluation

Macros allow you to implement lazy evaluation in an otherwise strict language, but this is not a comprehensive explanation of their utility.

Second. I've watched pretty much all of Rich Hickey's presentations a half dozen times or more. Somewhere in the mix of more practice, another time through SICP, and watching Rich Hickey I started getting what homoiconicity means and the value prop of Lisp.

It's simultaneously very prosaic and very deep. Macros are pretty simple:

1. Access to the AST of a chunk of code for arbitrary processing.

2. At compile-time.

This is not exclusive to Lisps. I think pretty much any mature language offers a library or a built-in mechanism for getting at the AST of arbitrary code in that language. The "at compile-time" part is not necessarily built in to other languages. You absolutely can have a build system that has two (or more) passes, the first building a macro expansion framework and library of macros and the second processing the rest of your source code and rewriting the AST of the code in those files to do macro expansion.

Where Lisps are different:

1. The AST of the language is represented directly in the core data structures of the language. An operation in Lisp[0] is simply a list whose first element is an operator (one of a special operator, a macro, or a function in most Lisps) and whose remaining elements are operands to that operator. This makes AST manipulation easier, because you use the same functions/abstractions/interfaces as in normal programming, because you're directly using familiar data structures.

2. Separation of reader and evaluator (interpreter or compiler or either/both). The reader transforms textual representations into in-memory representations of values and data structures. The evaluator only ever receives these in-memory representations. Thus, it's easier to do programmatic manipulation of an AST. You never have to do code-gen of emitting source code glyphs to feed into a compiler that expects text input. Once past the reader, everything is an in-memory representation, so we never have to get back to text. That said, it's trivial to get back to text if you want, because Lisps also include a printer which takes an in-memory representation and prints the glyphs that represent their readable (i.e., can be read by the reader) textual representation.

3. The evaluation model explicitly supports tagging some code as a macro to go through macro expansion before being evaluated "normally".

4. It is idiomatic to use macros to solve code re-use problems that cannot be readily handled by standard function calls.[1]

These four Lisp traits are not exclusive to Lisp, necessarily. As I mentioned above, you could build a macro expansion and code generation library that is part of a build process for any compiled language. Eval, in interpreted languages, is as expressive as Lisp macros, but is incredibly poorly supported and unergonomic in comparison to Lisp macros.

It's an ergonomic and feasibility thing more than a capability thing. That said, POSIX shell is turing complete, so you don't need anything like C# or Lisp or Haskell or anything else we've discussed to get your computation done. The reaction you might have to comparing shell scripting to Haskell is not dissimilar to the reaction an experienced Lisp programmer might have to comparing AST manipulation and code gen through a library to Lisp's macro facilities.

[0]: Clojure, specifically abstracts the idea of callable, and there are more callable things than special operators, macros, and functions.

[1]: Languages such as Haskell and OCaml continue to push the boundaries of what can be handled by "standard function calls" (which term I hesitate to use, given its imprecision). That said, given that macros allow the use of a turing-complete language to generate inputs to the evaluator, they allow you to implement any language construct that is computable without modifying the evaluation infrastructure.

Re: Understanding the Power of Lisp (2020)

#112
post #75

Earlier quoted context omitted.

If that were true I would consider it a very weird Common Lisp implementation. The main point is that you should never depend on it being true even though the two forms look the same. A couple more examples for the Common Lispers in the audience that highlight the difference between the reader and the evaluator: (setf foo (list #1=(list 3 4 5) #1#)) (eq (car foo) (cadr foo)) ; --> ?? What should we expect the second…

> If that were true I would consider it a very weird Common Lisp implementation. One of those is called SBCL. 'The main point' is that you can't depend on the value of (EQ '(a b) '(a b)) being NIL. Reason: a compiler compiles a Lisp file. The compiler sees literal data like '(a b) ' and '(a b). The compiler determines that these are 'similar' objects and 'coalesces' them into one object. It might even do it for '(a b…

  $ sbcl
  This is SBCL 1.5.8.138-d53fb0eb3, an implementation of ANSI Common Lisp.
  More information about SBCL is available at .

  *  (eq '(a b) '(a b))
  NIL
  *
Which is exactly what I'd expect in any normal (non-weird) Common Lisp implementation. Is your SBCL returning T for this?

Re: Understanding the Power of Lisp (2020)

#113

Earlier quoted context omitted.

Thank you for the examples. I'm really curious to get to the bottom of this. My inspiration has been Erik Meijer, specifically his article "Confessions of a Used Programming Language Salesman. Getting the Masses Hooked on Haskell" [1]. If I understand you correctly, you are implementing lazy evaluation in your when macro, something you call short-circuiting. By dropping laziness in strict functional languages such as…

I'm happy to chat more if it's helpful (contact details in profile). I'll be honest that I didn't get Lisp for a long time, and stilll wouldn't claim true expertise. I attempted SICP a few times based on the glowing reviews from folks I admire. One of my paths to tech was via Paul Graham's writings, so I had a bias for Lisp early on. Two parts here. First, and briefly a note on short circuiting and lazy evaluation. S…

> homoiconicity... is simultaneously very prosaic and very deep

That's what drives me on. That and Rich Hickey's enthusiasm and the sense that he knows something really valuable that I don't. I watch a lot of videos as well, so I can't remember who said that programming languages were user interfaces between human and machine and are an attempt to talk to machines to get them to do things. In that sense, programming languages are a lot deeper than syntax convenience or efficiency. A good language allows us to talk to machine at a much higher level, and I get from functional programmers that this is what they find so attractive.

I will contact you if I get really stuck and when I finally get it just to share it with someone. Thanks!

edit: I've heard a lot about SICP but never looked into it. I think it's time, now that I have time

Re: Understanding the Power of Lisp (2020)

#114
post #75

Earlier quoted context omitted.

> If that were true I would consider it a very weird Common Lisp implementation. One of those is called SBCL. 'The main point' is that you can't depend on the value of (EQ '(a b) '(a b)) being NIL. Reason: a compiler compiles a Lisp file. The compiler sees literal data like '(a b) ' and '(a b). The compiler determines that these are 'similar' objects and 'coalesces' them into one object. It might even do it for '(a b…

$ sbcl This is SBCL 1.5.8.138-d53fb0eb3, an implementation of ANSI Common Lisp. More information about SBCL is available at . * (eq '(a b) '(a b)) NIL * Which is exactly what I'd expect in any normal (non-weird) Common Lisp implementation. Is your SBCL returning T for this?

put it into a file, compile it, load it, run it:

  /tmp % more test.lisp

  (print (list :eq-or-not (eq '(a b) '(a b))))

  /tmp % sbcl
  This is SBCL 2.1.9, an implementation of ANSI Common Lisp.
  More information about SBCL is available at .

  SBCL is free software, provided as is, with absolutely no warranty.
  It is mostly in the public domain; some portions are provided under
  BSD-style licenses.  See the CREDITS and COPYING files in the
  distribution for more information.

  * (load (compile-file "test.lisp"))
  ; compiling file "/private/tmp/test.lisp" (written 03 JAN 2022 07:58:54 PM):
  ; processing (PRINT (LIST :EQ-OR-NOT ...))

  ; wrote /private/tmp/test.fasl
  ; compilation finished in 0:00:00.003

  (:EQ-OR-NOT T)
  T

Re: Understanding the Power of Lisp (2020)

#115
post #34

I have the same response to this as I do to every other lisp article. It’s cool, but how have you personally leveraged this advantage that everyone talks about? What big lisp project has the author contributed to, such that they have enough data to sing it’s praises?

I follow that up with knowledge of a large project that was started in Lisp (actually Scheme) that had to be converted to C# because the difficulty of finding experienced Scheme developers for the years of maintenance that would be expected was far more than the cost of converting to a language that's more "usable." Still remember all the meetings that generally always included someone complaining "what the $#&^%! we…

As long as “hire experienced X engineers” is a non-negotiable requirement, you better choose a popular value of X.

In parts of the community we do not so much think of the tools as part of the identity of the engineers. We’re looking for general intelligence/capability and train specific technologies on the job.

Re: Understanding the Power of Lisp (2020)

#116

Earlier quoted context omitted.

I'm happy to chat more if it's helpful (contact details in profile). I'll be honest that I didn't get Lisp for a long time, and stilll wouldn't claim true expertise. I attempted SICP a few times based on the glowing reviews from folks I admire. One of my paths to tech was via Paul Graham's writings, so I had a bias for Lisp early on. Two parts here. First, and briefly a note on short circuiting and lazy evaluation. S…

> homoiconicity... is simultaneously very prosaic and very deep That's what drives me on. That and Rich Hickey's enthusiasm and the sense that he knows something really valuable that I don't. I watch a lot of videos as well, so I can't remember who said that programming languages were user interfaces between human and machine and are an attempt to talk to machines to get them to do things. In that sense, programming…

I'll recommend the videos from the 1980s and this version of the text, which is set nicely and has working footnotes.

Videos: https://ocw.mit.edu/courses/electrical-engineering-and-compu...

Text: https://github.com/sarabander/sicp (links to epub and html versions at the top of the README)

Re: Understanding the Power of Lisp (2020)

#117

Earlier quoted context omitted.

> homoiconicity... is simultaneously very prosaic and very deep That's what drives me on. That and Rich Hickey's enthusiasm and the sense that he knows something really valuable that I don't. I watch a lot of videos as well, so I can't remember who said that programming languages were user interfaces between human and machine and are an attempt to talk to machines to get them to do things. In that sense, programming…

I'll recommend the videos from the 1980s and this version of the text, which is set nicely and has working footnotes. Videos: https://ocw.mit.edu/courses/electrical-engineering-and-compu... Text: https://github.com/sarabander/sicp (links to epub and html versions at the top of the README)

> I attempted SICP a few times

I don't have a computer science degree and I think that has held me back some, but I try to overcome it by always trying to learn and never thinking I should already know something or feeling like I'm an expert. I don't have the patience at my age to go back to school and get a degree. That you attempted SICP a few times is a good sign that you don't give up and speaks to your success in finally understanding.

Re: Understanding the Power of Lisp (2020)

#118
post #75

Earlier quoted context omitted.

> If that were true I would consider it a very weird Common Lisp implementation. One of those is called SBCL. 'The main point' is that you can't depend on the value of (EQ '(a b) '(a b)) being NIL. Reason: a compiler compiles a Lisp file. The compiler sees literal data like '(a b) ' and '(a b). The compiler determines that these are 'similar' objects and 'coalesces' them into one object. It might even do it for '(a b…

$ sbcl This is SBCL 1.5.8.138-d53fb0eb3, an implementation of ANSI Common Lisp. More information about SBCL is available at . * (eq '(a b) '(a b)) NIL * Which is exactly what I'd expect in any normal (non-weird) Common Lisp implementation. Is your SBCL returning T for this?

[deleted]

Re: Understanding the Power of Lisp (2020)

#119

Earlier quoted context omitted.

The existence of template metaprogramming is an argument for macros, not against them. It shows the lengths to which programmers will abuse and misuse a feature so that they can reach their goals.

When I first started writing asm code, it was accepted to write self-modifying code (programmatically create new code, then jmp to it.) Then I discovered and was told it was a bad idea. So that's how I view Lisp macros - creating code on the fly and executing it. When I was studying this issue, I came across femtolisp, which is a standalone Lisp by the author of Julia, Jeff Bezanson. Julia has metaprogramming so I wa…

> Professor Steve G. Johnson explains metaprogramming (around 10 min mark) and says "most of the time, don't do metaprogramming."

As someone who has given similar advice when teaching metaprogramming in the past, I'll add some context to that: most people new to metaprogramming seem to reach for it all the time for some reason, in situations where the usual programming constructs are perfectly adequate. Imagine if any time you introduce people to multithreading, they start writing all their code as parallel threaded code (which happens to some degree, but is limited by factors include what a PITA it is). That's the context in which such warnings are deemed necessary.

Just like multithreading, manual memory management, and other great-power-great-responsibility programming concepts, macros shouldn't be the first tool you reach for, but when you need them you really need them, and they can make your life so much easier.

Re: Understanding the Power of Lisp (2020)

#120
post #114

Earlier quoted context omitted.

$ sbcl This is SBCL 1.5.8.138-d53fb0eb3, an implementation of ANSI Common Lisp. More information about SBCL is available at . * (eq '(a b) '(a b)) NIL * Which is exactly what I'd expect in any normal (non-weird) Common Lisp implementation. Is your SBCL returning T for this?

put it into a file, compile it, load it, run it: /tmp % more test.lisp (print (list :eq-or-not (eq '(a b) '(a b)))) /tmp % sbcl This is SBCL 2.1.9, an implementation of ANSI Common Lisp. More information about SBCL is available at . SBCL is free software, provided as is, with absolutely no warranty. It is mostly in the public domain; some portions are provided under BSD-style licenses. See the CREDITS and COPYING fil…

Excellent example of a place where the file compiler can differ from the REPL, and as you point out this is perfectly legal. It's why #'eq is often not the best equality predicate unless you know what you're doing.

CCL and Lispworks return NIL for this test in both cases.

Post reply on HN