Live data from Hacker News

Ask HN: What Is the Lisp “Enlightment”?

news.ycombinator.com

31–40 of 45 posts

Re: Ask HN: What Is the Lisp “Enlightment”?

#31
In my experience people are talking about a couple of different things when saying Lisp enlightened them:

1. The realization that code and data are the same kind of entity. From this realization springs the realization that you can use data structures for managing code and that leads you to Lisp macros - which is one of the superpowers of Lisp. Lisp stands for LISt Processor and Lists are the primary data structure (okay, technically CONSes are, but let's not confuse things) for storing both data and code. Why all the funny parenthesis in Lisp? Because that's the syntax for a list data structure! '(1 2 3) is a list of the first three positive integers and (add 1 2 3) is a list whose first member is the symbol 'add' - which is a function. When evaluated it will yield the value 6. There - now you know Lisp syntax!

2. The realization that many algorithms can be easily implemented using a list data structure having two simple operators: first and rest (CAR and CDR in Lisp parlance) and recursion. This is why you see so many articles about implementing Lisp in . It doesn't take a lot to get a Lisp interpreter working, in fact it used to be a routine exercise for undergraduate CS students. Evaluating (car '(1 2 3)) yields 1, and (cdr '(1 2 3)) yields '(2 3). A recursive function in Lisp then typically has two parameters, a list and an accumulator. If the list is NIL then you return the accumulator, otherwise you update the accumulator by applying some operation to the CAR of the list and the input accumulator and recursively invoke the function with the CDR of the list and the updated accumulator. Simple. Tail-call optimization also makes this highly-performant.

3. The difference between symbols in Lisp and variables in other programming languages. In many commonly-used programming languages variables refer to memory locations. That's not strictly true in Lisp. Symbols are an item in a symbol table and the current symbol table is context-sensitive. That means symbols have properties, including user-defined properties, and the same code may be executed with different symbol tables in effect - which is another part of how macros can be so powerful.

4. Functions are first-class objects. This is also different from many other commonly-used programming languages, though that has been changing. A symbol can evaluate to a function rather than a value, functions can be created on the fly (lambdas) and passed as parameters to other functions. You can have a list containing functions (such as (add 1 2 3) from above) and evaluate those functions and so forth. A function is simply another data type. This was more of a big deal 10 or more years ago, now many languages have been adding this capability.

5. The REPL. It's because code and data are the same construct that the REPL as you know it can exist. Even better is when you realize not all functions in the REPL have to be interpreted - you can compile them! You can freely intermix interpreted and compiled code together. You can load compiled code into your session, add new code, modify existing code - whatever you need to do. It's an extremely creative and productive development environment.

That's it! That's the Lisp enlightenment.

Re: Ask HN: What Is the Lisp “Enlightment”?

#32
I'd say that Common Lisp is still the ultimate weapon of a solipsistic developer, with unprecedented raw power for reinventing the world from scratch without collaborating with others. For a cohort of people, this feeling can be very liberating and enlightening.

In my opinion, most modern dynamically-typed languages are actually better than Common Lisp in almost all regards that matter for social, collective programming:

- they are usually much more functional and declarative: more immutability, more value-oriented approach (Erlang, Clojure), less unpredictable mutation, more useful abstractions built into the language. They provide modern data structures in standard libraries in a useful and uniform way.

- they provide a builtin class/object/prototype common language for their ecosystems (CLOS in Common Lisp and Moose in Perl 5 are examples of too flexible opt-in OOP systems that fragment ecosystems instead of unifying them); this also gives decent namespaces, which are crucial for cooperation between teams.

- they usually have a good async story (iterators/generators/coroutines, promises/futures, async/await), again, built into the language and enabling async ecosystems on top of them.

- they have good(-ish) story about package management. Lispers seem to like to write everything from scratch.

- they have good story about testing infrastructure. Lispers are usually too proud of keeping the world in their heads and of their REPL-driven bug-fixing dopamine cycle to actually think about having computer test their code. The ability to change a running program from the inside is actually a detriment to abstract thinking about what code is meant to do and describing that in more declarative invariants like type systems and tests.

- tooling has finally caught up: with LSP and treesitter, programmers do not have to bear the pain of S-expressions to have structural editing anymore. A sufficiently educated programmer recognizes syntax trees as a valid data structure in any language. If really needed, modern languages do provide abstractions to manipulate their AST (`import ast, dis` in Python, `erl_syntax` in Erlang, `ast` in Go, declarative and procedural macros in Rust, Template Haskell, etc).

The only thing which I think Common Lisp still does better is conditions/restarts, which seems a better way of error handling than just catching exceptions.

Re: Ask HN: What Is the Lisp “Enlightment”?

#34

Lisp/Clojure allows you to solve/think about problems at a higher level, do a hacker rank puzzle in java/C++ and then do the same using clojure.

When I started learning Clojure, I often found it helpful to look at the solutions in Clojure for coding puzzles which I already knew mostly how I’d tackle in a c-family language.

The quality varies a lot: someone as new as I was would write a 30-line function using (for), then a few answers down I’d find a 3-line answer that used (frequencies), (juxt), or some other useful standard library function that I hadn’t discovered yet.

Clojure differs from other lisps (from what I can see; haven’t spent much time with the others) with its emphasis on immutability-first, and by building a ton of functionality around the seq abstraction. Judging by the comments here, other lisps place more emphasis on meta programming. While “code is data” remains true and powerful in Clojure, I have not yet had to write a macro for myself. The homoiconicity is still quite nice.

The chunk of lisp enlightenment I have a more sound claim to after learning Clojure is the power of the REPL. The ability to access any function, at any level of a running system, feed it any data to see how it responds, then change the function and see it immediately reflected without restarting anything, results in a very tight feedback loop that you won’t realize you missed elsewhere.

Re: Ask HN: What Is the Lisp “Enlightment”?

#35
post #9
post #3

For me Lisp enlightenment was when I realized everything is lambda all the way down (except of course for lists and atoms). I mean we don't really need all the various fancy let, let*, defun expressions to do programming. let, defun are all just abstractions on top of LAMBDA expressions. That means that we don't need some 30 odd keywords to bootstrap a language like we do in C. We can bootstrap a language implementat…

> lambda all the way down (except of course for lists and atoms) List and atoms can be defined in terms of lambdas as well (see e.g. [1]). You don't need any special forms at all. Perhaps we should talk about Lambda Enlightenment? [1] https://www.ioccc.org/2012/tromp/hint.html

Loved reading this. Thank you.

Re: Ask HN: What Is the Lisp “Enlightment”?

#37
post #19

Earlier quoted context omitted.

For me the most “woa” thing about LISP and its macros is that code is data, and data is code. The representation of a (nested) list is exactly the same as actual code, and using macros, I can manipulate it as such. That is pretty profound, and I still have yet to find a language that just makes it so simple and elegant as with LISP.

Interactive, live development in Common Lisp is also basically unknown outside of maybe smalltalk. Stuff like "hot reload" in Javascript development is not at all comparable.

People always forget about Forth!

Re: Ask HN: What Is the Lisp “Enlightment”?

#38
post #19

Earlier quoted context omitted.

For me the most “woa” thing about LISP and its macros is that code is data, and data is code. The representation of a (nested) list is exactly the same as actual code, and using macros, I can manipulate it as such. That is pretty profound, and I still have yet to find a language that just makes it so simple and elegant as with LISP.

Interactive, live development in Common Lisp is also basically unknown outside of maybe smalltalk. Stuff like "hot reload" in Javascript development is not at all comparable.

Smalltalk...

Re: Ask HN: What Is the Lisp “Enlightment”?

#39
post #23

While it's part of homoiconicity, I'd stress that a great part of the language is that it grows without warts. Because the "syntax" is so insanely regular, new layers of abstraction look just like the builtin layers. With something like SBCL, you can literally write assembly in lisp notation (called VOP in SBCL), then layer the Common Lisp language on top seamlessly, then layer your domain-specific code on top with y…

Yes, it's almost as much a mathematical notation for lambda calculus as it is a programming language.

Re: Ask HN: What Is the Lisp “Enlightment”?

#40
This is what enlightenment looks like: https://aphyr.com/posts/353-rewriting-the-technical-intervie...

At the risk of explaining the joke and ruining it: the author is a transcendental being doing a fizz-buzz coding interview. The interviewer doesn't like the fact that the author copied and paste in a solution....so the author then goes off and writes an interpreter in Clojure (Clojure is a Lisp) that actually interprets the original code snippet.

---

What I actually love and enjoy about Lisp (Clojure in particular) is how malleable it is. Functional Programming is the paradigm you hear about the most, but you can write object-oriented code (lambdas simulate objects very easily, see SICP) or Logical programming (clojure/core.logic) or, ultimately, imperative programming.

Adding new "features" to the language is something every Lisp intro book should include. Don't have an `until` operator? (the opposite of a `while`) bam: macro. It's trivial.

Want to have goroutines? There's a library: core.async. Languages like Go get popularity because of their features like goroutines, Lisps can just import a library and get the same (or similar).

I'll fully admit, I bounced off Clojure for the first three times I tried learning it. But when it clicks is when you assemble a handful of map/reduce/apply and transform your data with ease, instead of shoving it around. Lisp makes your functions and data sing. Lisps allow you to find higher levels of composing functions, e.g. Transducers (https://clojure.org/reference/transducers)

Post reply on HN