Live data from Hacker News

Why Lisp? (2015)

blog.rongarret.info

81–90 of 174 posts

Re: Why Lisp? (2015)

#81
post #30

That article partially validates my idea on why some people think that Lisp is such a force multiplier. The idea would be that compilers are one of the most important tool productivity-wise, and that Lisp allows you write your compilers yourself. That would also explain why not Lisp: First, libaries are the new important tool for productivity, and any language can have that. Second, a shared understanding is very imp…

> Shared understanding is important for building and maintaining software.

I think this is where Go (the language) really shines. Go is "boring" -- there are no macros, no operator overloading, no default arguments, none of that sort of thing. But if your goal is shared understanding, "boring" is a compliment. "Boring" means "after using the language for a few years, I can be confident that I will never be surprised by a piece of Go code again." (This has been true for me for at least 4 years.)

In the same vein, Go famously doesn't support user-defined generics; the only generics it has are reified "builtins," baked into the language. People gripe about that, but the upshot is that every Go programmer understands the semantics of those builtin functions. They're Schelling points! When user-defined generics are added in the next version of Go, I worry that we'll lose some shared understanding. It will be a subtle shift, but the language will feel a bit less "cozy." :/

Re: Why Lisp? (2015)

#82
post #62

I started my programming in the more "traditional" way i.e., C/C++ and then later python. When I started doing python, I was flabbergasted to find that I could just do 100**100 and get a complete number without using any additional libraries. Similarly, reversing string was trivial using the [::-1] notation. Heterogeneity of Lists, Dictionaries and the resulting versatility blew my mind. I now understand that I was d…

I don't know much about Lisp. But you do realize you're asking sort of the following right?

Roman: what's so amazing about the decimal system? I can do addition just fine with X + X = XX.

Modern human: Well... you can do negative numbers easily, irrational numbers, you can show zero easily.

Roman: bah! I don't know what you're talking about. Half those numbers don't even exist.

Mathematician: actually have you heard about complex numbers?

The point is: you have to learn a new way of thinking compared to procedural/OOP style programming. And it might be the case that the problems it solves are not practical for your use-case.

I tell you, as a non-Lisp programmer, what I find amazing about Lisp. Disclaimer: I may be partially or completely wrong. I'm really not the one who should be writing this, but since our knowledge of Lisp isn't that far apart, I might be able to empathize with your perspective a bit more since I have been in it.

* Your code is the abstract syntax tree! Don't know what an abstract syntax tree is? It's a data structure that compilers use in order to generate code. In normal languages you have to first parse the language, compile it to the abstract syntax tree and then generate machine code from it. However, in the case of Lisp, it's already there. So this means that you can write compiler-esque programs much easier.

* One of the nice things that a direct AST provides that it allows partial live reloading of code. Imagine this:

``` myAge = 17 if (myAge >= 18) { print("Nice!") } else { print("Hello youngster!") } ```

With Lisp, you can basically swap out the first print function for another function while the program keeps running.

Re: Why Lisp? (2015)

#83

Earlier quoted context omitted.

> Getting Lisp to run as fast as C takes major effort when at all possible. The Computer Language Benchmarks Game shows Lisp Code as generally being between 2x and 10x slower than C++[1]. As fast as C? No. Way faster than Python, and more than fast enough to be used in almost every single application, modulo hard-real-time systems and AAA video games? Yes. > Lisp needs a lot of space to do it's thing; and while it's…

> ... AAA video games? Yes. One of my favorite stories of the ideas of lisp commercially is a dialect that Naughty Dog developed for their game development. It started as being developed for Crash Banicoot on the PS1. Which is really interesting given how limited the system was (1mb to 2mb of ram depending on what you were doing with the system!) They later iterated on it for Jak and Daxter on the PS2. To quote the w…

> It really makes me wish some big company would make a python clone in lisp, but put a tiny escape hatch in to fully utilize the lispy parts.

It’s not from big company but that describes clpython: https://common-lisp.net/project/clpython/index.html

Re: Why Lisp? (2015)

#85
post #62

I started my programming in the more "traditional" way i.e., C/C++ and then later python. When I started doing python, I was flabbergasted to find that I could just do 100**100 and get a complete number without using any additional libraries. Similarly, reversing string was trivial using the [::-1] notation. Heterogeneity of Lists, Dictionaries and the resulting versatility blew my mind. I now understand that I was d…

To be fair, other non-Lisp languages have come a long way. Still, I'll offer a few things that might pique your interest.

For a while in the development of Common Lisp, a sort of joke acceptance test for implementations was in three parts: 1) you type T and enter into the REPL, it responds T 2) you define the factorial function and calculate (/ (factorial 1000) (factorial 999)) and it responds 1000 3) You try (atanh -2) and if it returns a complex number it passes (extra credit for the correct complex number). Lisp has a great numerical tower. Besides being able to deal with huge and complex numbers, you also have convenient syntax for specifying numbers in base 2 and 16, and you can do things like bitwise operations on bit-vectors:

    (bit-and #*00110100
             #*10101010)
    -------> #*00100000
For macros, there's a lot of cool ones. Unlike C macros that just do string substitution, Lisp macros let you use the full Lisp language to write code that does something with expressions passed to the macro. A nifty one that's part of basically every implementation with uiop:nest is described here https://fare.livejournal.com/189741.html The problem it solves is that sometimes in Lisp you'll have a lot of nested forms, and your code begins to attack the bottom right corner of your screen. e.g.

    (multiple-value-bind (a1 b1 p1) (foo1)
      (with-open-file (f1 p1 ...)
        (let ((x1 (read f1)))
          (when x1
            (multiple-value-bind (a2 b2 p2) (foo2)
    ...
The macro lets you rewrite that as

    (nest
     (multiple-value-bind (a1 b1 p1) (foo1))
     (with-open-file (f1 p1 ...))
     (let ((x1 (read f1))))
     (when x1)
     (multiple-value-bind (a2 b2 p2) (foo2))
    ...
The macro definition is really straightforward:

    (defmacro nest (&rest r)
     (reduce (lambda (o i) `(,@o ,i)) r :from-end t))
(The list of forms are passed, unevaluated and at compile time, to nest, which rewrites them using a right fold to nest things properly.)

Somewhat similar is the arrow macro that Clojure popularized, which lets you get rid of (deep (nesting (like (this ...)))) where you have to remember evaluation order is inside-out and replace it with a flatter (-> (this ...) like nesting deep). Or (loop (print (eval (read)))) -- which will indeed give you a primitive REPL within Lisp -- with the more readable (-> read eval print loop). Its implementation is also easy -- many macros are easy to write because Lisp's source code is itself a list data structure for which you can write code to process and manipulate just like any other lists.

Another cool macro that's been around since 1993 is https://github.com/quil-lang/cmu-infix which lets you write math in infix style, e.g. #I( C[i, k] += A[i, j] * B[j, k] ) where A, B, and C are all matrices represented as 2D arrays. It's a lot more complicated than the nest macro, though.

There are some other things that still make Lisp great in comparison to other languages, but they don't exactly have one-line code examples like [::-1] and so I'll just describe them qualitatively. Common Lisp has CLOS, the first standardized OOP system. It's a lot more powerful than C++'s system. It differs from many systems in that classes and methods are separate; among other things this gives you multiple dispatch (you can define polymorphic methods that don't just dispatch to different code depending on the first argument (the explicit 'self' in Python, implicit 'this' in other langs) but all arguments). One thing it can be useful for is to get rid of many laborious uses of the Builder and Visitor patterns. e.g. the need for double dispatch is a common reason to use the Visitor pattern, but in Lisp there's no need. CLOS also does "method combination", which lets you define :before, :after, and :around methods that operate implicitly before/after/around a call. This gets rid of the Observer pattern, supports design-by-contract, and jives well with multiple inheritance in that you can create "mixins" that classes can "inherit" from with the only behavior being some :before/:after methods. (e.g. logging, or cleaning up resources, or validation.)

Everything is truly dynamic -- an object can even change its type at runtime, which may be an acceptable solution to the circle-ellipse problem, or just super convenient while developing. More fundamentally, "compile" is a built-in function, not something you have to do with a separate program. "Disassemble" is built-in, too, so you can see what the compiler is doing and how optimized something is. You have full flexibility to define and redefine how your program works as it's running, no need to restart and lose state if you don't want to. Besides being killer for development (and all the differences in development experience comprise a big part of why I still think Lisp is great compared to non-Lisp), this gives you a powerful way to do production debugging and hot-fixing too -- a footgun you might not necessarily want most of the time, but you don't have to do anything special for it when you do want it. It can be very useful, e.g. if you've got a spacecraft 100 million miles from Earth https://flownet.com/gat/jpl-lisp.html I've also put some hobby stuff on a server, just deployed as a single binary, but built so that if I want to change it, I can either stop it, replace the binary, and start again, or just SSH in and with SSH forwarding connect to the live program with my editor and load the new code changes just like I would when developing locally, and thus have zero downtime.

Lastly, Lisp's solution to error handling goes beyond traditional exception handling. Again this ties into the development experience -- you have some compile-time warnings depending on the implementation (e.g. typos, undefined functions, bad types) but you'll hit runtime errors eventually, Lisp provides the condition system to help deal with them. It can be used for signaling non-errors, which has its uses, but what you'll see first are probably unhandled errors. By default one will drop you into a debugger where the error occurred, the stack isn't immediately unwound. Here you can do whatever -- inspect/change variables on different stack frame levels, recompile code if there's a way to fix things, restart computation at a specific frame... You'll also be given the option of "restarts", which might include just an "abort" that unwinds to the top level (possibly ending a thread) but can include custom actions as well that could resolve the error in different ways. For example, if you're parsing a CSV file and hit a value that is wrong somehow (empty, bad type, illegal value, bad word, whatever), your restarts might be to provide your own value or some default value (which will be used, and the computation resumes to parse the next value in the row), or skip the whole row (moving on to the next one), or skip the whole file (moving on to the next file, or finishing). Again this can be very useful while debugging, but in production you can either program in default resolutions (and a catch-all handler that logs unhandled errors, as usual) or give the choice to the user (in a friendlier way than exposing the debugger if you please).

Re: Why Lisp? (2015)

#86
post #80

I love Lisp and Scheme and all their relatives (Clojure, Logo, Racket, etc.). However, the fact of the matter is, Common Lisp has not kept up with modern developments in terms of presenting a cohesive ecosystem with forward momentum. Everybody is off on their own doing their own thing with no shared goals or cohesion. Clojure seems to have this (I have not used Clojure much, so I don't really know). Elixir definitely…

> ... it goes well beyond any Lisp/Scheme (and many modern languages) in terms of having a practical but expansive ecosystem with a strong set of idiomatic conventions.

Does it, really?? Common Lisp is an ANSI Standard, I don't know how you get a more "strong set of idiomatic conventions" than that. And it's been "done" for 20 years now! People continue to use it just as it is, and there's very little pressure to change it because it simply works!

You're trying to justify a new wave of "modern" languages with a very weak argument... just call it for what it is: you want "cool", hyped languages to succeed despite the fact they offer nothing that the CL standard did not 20 years ago.

Re: Why Lisp? (2015)

#87
post #62

I started my programming in the more "traditional" way i.e., C/C++ and then later python. When I started doing python, I was flabbergasted to find that I could just do 100**100 and get a complete number without using any additional libraries. Similarly, reversing string was trivial using the [::-1] notation. Heterogeneity of Lists, Dictionaries and the resulting versatility blew my mind. I now understand that I was d…

Given that it's a 'new paradigm' and enhances 'intuition', it's a bit hard to show simple examples which demonstrate that. You'll have to put in the work.

Will you be able to understand how to play the guitar if I show you a passage from a song ? You can play it with any other instrument, so it still won't be motivating enough for you to put in the time and effort required to learn it.

If I were to take you to a Led Zeppelin concert to see Jimmy Page playing, that still might not be motivation enough for you to pick up the guitar, but you'll be able to better understand what it can do to people :).

Back to LISP - it's a tool. The difference between other tools, is that you can program it too. The power lies in its simplicity and your ability to build programs iteratively while running them.

Re: Why Lisp? (2015)

#88
post #56

I think at some point we should clarify why we mean by Lisp. I see a lot of mention of Clojure, while most people seem to assume that Lisp == Common Lisp. I feel like talking about Common Lisp, Racket, Scheme, Clojure at the same time while putting them all under the "Lisp" umbrella seem to be a bit pointless when we're talking about languages. It's like mentionning JS in a conversation about C because "JS is a langu…

I look at it as an intersection between the feature sets of Common Lisp, Clojure, Scheme, etc. In other words - the most basic features of these languages that are common - S-expressions, data is code is data, REPL-driven development, etc

Re: Why Lisp? (2015)

#89
post #30

That article partially validates my idea on why some people think that Lisp is such a force multiplier. The idea would be that compilers are one of the most important tool productivity-wise, and that Lisp allows you write your compilers yourself. That would also explain why not Lisp: First, libaries are the new important tool for productivity, and any language can have that. Second, a shared understanding is very imp…

> Shared understanding is important for building and maintaining software. I think this is where Go (the language) really shines. Go is "boring" -- there are no macros, no operator overloading, no default arguments, none of that sort of thing. But if your goal is shared understanding, "boring" is a compliment. "Boring" means "after using the language for a few years, I can be confident that I will never be surprised…

I take a different view, that shared understanding comes from clear, concise code, which is not bogged down in ceremony.

Lines of Go in isolation are easy to understand. However, Go is so lacking in expressiveness, that Go code-bases are hard to follow for any complex domain. The language doesn’t give you enough tools to tame complexity.

Re: Why Lisp? (2015)

#90
post #80

I love Lisp and Scheme and all their relatives (Clojure, Logo, Racket, etc.). However, the fact of the matter is, Common Lisp has not kept up with modern developments in terms of presenting a cohesive ecosystem with forward momentum. Everybody is off on their own doing their own thing with no shared goals or cohesion. Clojure seems to have this (I have not used Clojure much, so I don't really know). Elixir definitely…

I'm not exactly a Lisp fan (I'm firmly in the statically typed camp), but this is a weak argument. Common Lisp specifically is one of the most cohesive experiences one can find out there, the language has had a long time to mature, it's ANSI standardized and the ecosystem is largely built on top of that cohesive base.
Post reply on HN