Hacker Timesnew | past | comments | ask | show | jobs | submitlogin

This article doesn't use the name "Lisp" enough. The language with the best chance of lasting a long time is the one with the simplest syntax. That is Lisp. It is already one of the oldest programming languages and the lisp family of languages are still mutually intelligible to each other despite being large. The language core hasn't settled and the rest of the programming community has nearly caught up with the Common Lisp standard library.

Will a Lisp ever be the most popular language? Probably not. Maybe. Will they last 100 years? Easily. One or multiple of the current Lisps will still be there. If computers exist in 2123, someone will be making money using a current Lisp. Hopefully they'll be using one that has discovered the words "first" and "rest".



Personally, the biggest benefit of lisps is that features to the language can be added by not only the language designers, but by the users.

JavaScript wanted to add the synthetic sugar for async/await, so the language had to be redesigned with that in mind. In a lisp, async/await could be implemented by a library instead, keeping the core still small and nimble.

This of course is also a foot gun when used improperly, where some codebases can be harder for new developers to grok everything, as they basically have to learn a new language built on top of the standard language. But I've found that if you keep the "new language features" to a minimum and have good documentation, developers quickly get onboard and start to be productive.


That footgun doesn't seem to be a huge problem. The culture around macros seems to be "avoid if possible, use if necessary". It just becomes a skill like any other related to programming.

The alternative is _extremely costly_ in comparison. Code generators, transpilers, configuration, tooling, bespoke IDE features... All of that, diverging from each other in the most idiosyncratic ways and it all needs version control, RFCs, release management, documentation, design and development, breaking changes...

But with an extensible language you have all of this for basically free. People just make things, share things and the most useful and stable things bubble up.


The culture around macros should be that if you're making any kind of halfway complex library, you better have macros to simplify the common uses, and make the code shorter and more readable.

The users of the library having to write those macros is the negative situation; anticipate the kinds of macros users will want, and provide them.

If you use general macros over basic Lisp, try to use famous ones. E.g. for gensyms, use with-gensyms. If you don't use Norvig's or Graham's exact implementation, at least make yours 100% compatible.


Javascript programmers have to learn frameworks. You cannot escape from the fact that programs extend the language.

When you write a function, or define a new type, you're extending a language.

In human languages, nobody would dispute the idea that the formation of new nouns and verbs extends the language.

Lisp is kind of like a language where you can add a new conjunction and preposition, not just a new noun or verb. To the uninitiated, that somehow seems like it must be harder.

The fundamental problem is that you have some piece of syntax, and it denotes some process (or whatever) which is entirely implicit. Whether that syntax is a new kind of macro-statement or function call is immaterial. If you have no idea what it is, you have to go to the definition.

The behavior behind a single function call can be mind-bogglingly complex.


I recently returned to a Common Lisp program I have not touched for 9 years. It makes some use of the regex engine cl-ppcre (the > nine-year-old-version planted into the program), and I had to touch a few bits of code where I needed to introduce some new uses of that.

I had made a number of uses of a macro called register-groups-bind and just from looking at my own old uses of the macro, I was able to figure out how to use it again.

It is easier to use than most of the cl-ppcre API!

   (register-groups-bind (x y z)
                         ("regex..." input [options...])
     ;; here, x, y, z are bound to groups from the regex
    )
I needed to classify an input which could look like 123, 98% or 3/4. Integer, integer percentage or ratio. Easy:

  (register-groups-bind
    (num denom percent count)
     ("^(\\d+)/(\\d+)$|^(\\d+)%|^(\\d+)$" nq :sharedp t)
     ;; code here ...
  )
In the code, if count is true, we got the integer. If num is true, so is denom and we have the fraction. If percent is true, we got the percentage.

CL-PPCRE exposes the low-level objects: scanners you can create with create-scanner and use via the scan generic function. Using that would be a lot harder and verbose than the friendly macro.

My old code was easier to understand and maintain for me because of the easy macro.


> JavaScript wanted to add the synthetic sugar for async/await, so the language had to be redesigned with that in mind. In a lisp, async/await could be implemented by a library instead, keeping the core still small and nimble.

Sure, but try hacking in infix notation or more complex notations like list[index] access, and you'll quickly see why hacking that stuff in is a bad idea. Lisp severely punishes adding syntactic sugar if it diverges at all from prefix s-expressions. Look at Clojure's member variable access for a real life example of how this plays out.

And if we're willing to make concessions that our syntactic sugar can only be as sweet as the overall design of the language allows, I think it makes sense to concede the same to Javascript and admit that Promises existed as libraries for years before async/await, and worked just fine in lots of production code.


> The language with the best chance of lasting a long time is the one with the simplest syntax.

If you're going to make an argument for Lisp, I think focusing on syntax is the weakest argument you could make. Simplicity is good, sure, but syntactic simplicity is a very surface-level form of simplicity. Consider:

    (def inc (n)
      (+ n 1))

    def inc(n):
      return n + 1

    fn inc(n) {
      return n + 1;
    }

    inc(N) -> N + 1.
These are fictional example syntaxes, but you can see where my inspirations come from. The point is, these all express the same things. There's some argument about which syntax is clearest, but that's mostly going to be based on what languages people already know. It's a bit silly to argue what's clearest from some sort of ideal pure world where people coming into your language don't know any other languages, because that's not the world we live in.

Now consider:

    (def void incAll (((list int) ns))
      (decl int i)
      (for (= i 0) (< i (length ns)) (++ i)
        (++ ([] ns i))))

    def incAll(ns) {
       return map(ns, n => n + 1);
    }
In the first example, we're doing C-ish things in Lisp-ish syntax, and in the second example we're doing Lisp-ish things in C-ish syntax. As you can see, doing Lisp-ish things in C-ish syntax works pretty well (and lots of modern languages do this). But doing C-ish things in Lisp-ish syntax is an abomination--in fact, the simpler syntax actually forces us to do more weird stuff to get around not having more complex syntax for more complex operations.

This gives us a clue that maybe simple syntax isn't inherently simpler to use. At least some of the simplicity of Lisp comes from the other ideas it brings to the table. And notably, nothing prevents us from using those ideas in other languages.

Discussion of Lisp syntax can't fail to mention that Lisp's simpler expressions enable its powerful macros. Lisp true believers will wax poetic about how Lisp macros allow you to create domain specific languages. But in practice, macros are are often just an opportunity to shoot yourself in the foot and create hard-to-debug bugs. If you're introducing macros, the simplicity argument starts to fall apart because you're adding a massive amount of complexity.


Macros are one thing that you easily get from that syntax. And I would argue that it's far less of a footgun than you make it out to be.

But really there are many things that aren't mentioned such as Editor/IDE tooling, in-editor REPL, evaluating expressions, debugging, structural editing, code navigation, formatting...

Your example is actually kind of misleading, because the second variation is much closer to how you write in a Lisp than the first.

It would really be something like:

```

(def inc-all (partial map inc))

```

It really makes no sense to use a Lisp in a non expression based manner. The syntax is inherently optimized for it.


> Your example is actually kind of misleading, because the second variation is much closer to how you write in a Lisp than the first.

That's the point, yes. See how maybe the syntax isn't the important thing here?


It's syntax that is optimized for expressions rather than statements. There are a lot of nice features falling out of that. In my opinion it makes little sense to misuse it in order to show its utility. It's a little bit like quoting someone out of context if that makes sense.


> It's syntax that is optimized for expressions rather than statements.

I'm not sure how you think S-expressions are any more optimized for expressions than, for example, infix notation. That's exactly what I'm showing with the second example. I'm open to hearing why you think S-expressions are better for expressions, but I suspect any differences you might point out are pretty subjective.

> There are a lot of nice features falling out of that.

Really I can only identify one feature which falls out of S-expressions, which can't be obtained with other syntaxes: macros. And whether that's a good feature is pretty subjective: it's pretty arguably a misfeature in my experience.

> In my opinion it makes little sense to misuse it in order to show its utility.

Agreed: I'm not misusing S-expressions to show their utility.

I'm misusing S-expressions to show that they don't inherently give you the benefits of Lisp. And I'm showing Lisp-y code without S-expressions to show how the benefits of Lisp can be obtained without S-expressions.


> I'm not sure how you think S-expressions are any more optimized for expressions than, for example, infix notation.

They aren't. But they are optimized to represent expressions as simple data: nested lists. One could write infix expressions as a data structure:

(a + b * (c + d) * 3)

READ converts it into a list. For that one could write a simple evaluator, a simplifier, or other tools based on simple list processing.

> Really I can only identify one feature which falls out of S-expressions, which can't be obtained with other syntaxes: macros.

Macros are one application of processing of s-expressions. The more general feature is representing any type of code as nested lists -> symbolic expressions. They are a compromise between a human-readable, textual & internal data format and machine processable code. Lisp code is just one application of that. It could be Prolog code, expressions of a theorem prover, or anything else which sports a custom processing engine and a lean way to input/process/output code as data.

Actually macros can be obtained with other syntaxes. The Lisp way to represent code externally and internally as s-expressions is just one way. It's relatively simple and primitive. A simple interpreter for Lisp code can be written in a page of Lisp -> processing engines can also be very simple. The input and output of code then already provided by the reader and printer of s-expressions.

Thus one can use s-expressions to design notations that are BOTH trivially a data structure with a textual representation AND processable with a simple predefined 'list processor' (aka Lisp) or any other custom "list processor".

Looking at the textual representation in isolation is missing the bigger picture: it's the simple textual representation of lists AND a simple "list processor".


To my mind nothing can be as readable as:

  map some-function of some-parameter, other-parameter to some-parameter apply play-with other-parameter
Which is a more casual way to express:

  def some_function (  some_parameter, other_parameter )= some_parameter .     play_with other_parameter
The latter is actually valid Ruby code, but the former is not valid in any programming language I’m aware of. Yet they are a simple token substitute version of each other. I purposefully placed spaces in the latter to better reflect that.

Note that going a tiny bit further, you could easily get rid of the "apply" token in the former with some convention regarding precedence in denotation.

And yet the whole industry prefer to clutter their communicated ideas with all kind of symbols that are impenetrable to the layman.


Just for fun, have a look at Ngram results for some of these programming language name

https://books.google.com/ngrams/graph?content=Lisp%2CRuby%2C...

Of course, this doesn’t mean much, as Ruby and Python will most likely have a huge hit count, if not most, unrelated to programming langues. That also a lesson for naming programming languages, I guess. As everything vaguely named in it, C is really awkward on this regard.


What is Lisp used for in production these days? The wiki didn’t really specify, just that it’s connected to mathmatics and AI research.


Lisp is a language family. Popular, well known choices include for example: Common Lisp, Scheme, Racket, Emacs Lisp and Clojure.

There are countless production systems written in these languages, ranging from embedded, to web apps to infrastructure tooling. The specific domains where they're applied, are just as diverse as one could imagine.

The more interesting question is "Why would someone use any of these languages?".

Niche languages are typically associated with risk in the business world. But the thing is that Lisp just keeps surviving, evolving and finding new problems and domains to tackle.

My personal opinion is that these languages represent the powerful combination of freedom, stability and engagement.

A Lisp is inherently non-condescending as it gives you more powerful tools than most other languages, but it's also very reliable because it's built on a very well understood, minimal foundation. Last but not least you are programming in a way that is very engaging. You are right there in the running program.


> A Lisp is inherently non-condescending

My guy, you just didn't answer his question, and then proposed a question you thought was more interesting.


You're right it is a typical case of me trying to give a too general answer but then also getting side-tracked.


Don’t worry any answer gives me a bit more insight, and it was a natural follow up question probably :)

Just never heard of lisp beyond knowing it exists!


Some web apps also run Clojure (a lisp for the JVM) for backend and ClojureScript (Clojure compiled to JavaScript) for frontend. Probably Nubank ("largest fintech bank in Latin America") is the biggest company I know using Clojure in production in various ways.



This web site, for example.





Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: