Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why is Common Lisp not the most popular programming language? (daninus14.github.io)
130 points by kryptiskt on Feb 14, 2024 | hide | past | favorite | 332 comments


It's the lists.

No, not the prefix notation, parenthesis, what have you, although that doesn't help. The lists themselves. In Lisp, code is data, and data is lists.

Yes, of course, there are hashmaps, arrays, strings. But idiomatic Lisp code really does use linked lists extensively, it's an entire style of programming. Even if you'd prefer to use different data structures (and again, Common Lisp does support this), you still have to be able to read and reason about the many parts of the language designed for, as the old quip had it, prothething lithts.

The "weird looking syntax" does not help, but Lisp hackers are right that you get used to that part, might even appreciate it if macros are something you really care about. Structural editing a la paredit and parinfer are pretty nice too.

But when it comes with a weird way of programming, that's a bridge too far for a lot of people. It's harder to learn, read, and reason about, for longer than most languages.

I'm glad they mentioned Julia though. Learned everything Dylan had to teach and then some, real treat of a language, and, it turns out, writing macros without cons cells is rather pleasant and powerful. Certainly an acceptable Lisp in my book. Maybe someday they'll add conditions and restarts, that's the one feature CL which gives it an edge on Julia.


I didn't see mentioned in the thread (maybe missed it) the #1 reason, IMO, that Lisp can't be popular in companies.

Everyone should take the time to learn Lisp and do a handful of personal projects with it. It'll help your growth as a software engineer. Just do it!

But that doesn't make it a great corporate language. Lips is infinitely flexible, you can mutate it to be what you want. That's cool and feels awesome.

Also: a maintenance nightmare as soon as you have more than ~1 person working on the codebase!

Everyone surely has heard the joke/truism how C++ can be great as long as you only use a sane subset. But every team uses a different subset. Now imagine something like Lisp where every developer & team morphs it in a different way and you have a product with hundreds of developers on it. Try maintaining that without going insane.

The anti-Lisp is something like Go. Simple, not very flexible, everyone does it the same way mostly, you can plug & play developers like scrum demands we do.


> Also: a maintenance nightmare as soon as you have more than ~1 person working on the codebase!

Lisp isn't any harder to maintain than any other language. The Lisp codebases I've worked on, even professionally, were originally written by talented, experienced engineers and were in fact wonderful to maintain.

> Now imagine something like Lisp where every developer & team morphs it in a different way and you have a product with hundreds of developers on it.

Dr. Ian Malcom's could/should distinction applies here. Most Lisp teams do not "morph the language" willy-nilly. They set standards of what to do and what not to do, establish a house style, build up a library of in-house functions and macros, and the more junior programmers imitate the more senior ones, just like any other dev team in any other language. And the vast majority of Lisp teams are small, even if they are working on large applications.

"We wanted to make it possible for very large projects to be done by small teams of people and for smaller ones to be done by one person." --Tom Diaz, Director of Software Products, Symbolics, 1986: https://www.youtube.com/watch?v=-K01FQ73xgY&t=144s

> The anti-Lisp is something like Go. Simple, not very flexible, everyone does it the same way mostly, you can plug & play developers like scrum demands we do.

I think you might be on to something. Symbolics was very much in danger of making large software projects possible by one person or a small team of people. It seems as if the corporate world has responded to the proliferation of more powerful software development tools -- not only in Lisp, but certainly Lisp and Smalltalk had an outsized influence -- by lowering the skill ceiling to make devs more fungible, and creating more and more process to hobble their productivity so as to justify larger teams of devs and dev-adjacent personnel: PMs, POs, scrum masters, etc.


> Symbolics was very much in danger of making large software projects possible by one person or a small team of people.

Just that there were very few of these people. A typical saying: "there were more Lisp Machines (~ 10000 were made in one decade, over all Lispm companies) than programmers for them."


> The Lisp codebases I've worked on, even professionally, were originally written by talented, experienced engineers

But companies have untalented junior engineers, you need a language they can write while still remaining structured.

Talented professional engineers can write assembly language in a very easy to read and structured manner. But there is a reason basically everyone writes in structured languages today instead of unstructured, that enforced structure helps make code more readable and uniform.


With good leadership, untalented junior engineers can become very productive junior engineers. I've seen it happen. Within weeks some kids fresh out of college, who had been taught everything mainly in Java (this was still the JavaSchool era, more or less) were proficiently and happily contributing to our C++ code base. They were pretty good, but they weren't Carmack or anything.

Now if your leaders are untalented, you have an organizational issue: you're promoting schlubs. Why are you doing that? Stop doing that.


> I think you might be on to something. Symbolics was very much in danger of making large software projects possible by one person or a small team of people. It seems as if the corporate world has responded to the proliferation of more powerful software development tools -- not only in Lisp, but certainly Lisp and Smalltalk had an outsized influence -- by lowering the skill ceiling to make devs more fungible, and creating more and more process to hobble their productivity so as to justify larger teams of devs and dev-adjacent personnel: PMs, POs, scrum masters, etc.

This is a ridiculous LISP conspiracy theory that really needs to die. What world are you living in that corporations actively want their employees to work slowly or to have too many employees? Who does that benefit?

If lisp were genuinely powerful, corporations would be able to make bigger projects faster.


> What world are you living in that corporations actively want their employees to work slowly or to have too many employees?

Many companies are risk-averse and prefer predictability to performance. They therefore often choose slow and outdated software products instead of newer, more efficient ones and they prefer using tools that require large amounts of developers, but in a predictable fashion. Using a tool that allows a very small team to perform the job of 10x as many is risky because the bus factor becomes that much higher, as well as the difficulty in finding and training replacements.


> What world are you living in that corporations actively want their employees to work slowly or to have too many employees? Who does that benefit?

1) In the corporate world, a larger headcount under you means you get more funding for your initiatives. If you get things done with a team of five, that signals to upper management that that's all you need and your budget will be adjusted down accordingly.

2) When it comes to software, companies favor the ability to monitor and control the development process from above over speed. Agile only gets uptake among large orgs inasmuch as Agile consultants promise them this.

3) There is, decades after The Mythical Man-Month, a sense that you can get a good programmer's worth of output -- without a good programmer's worth of risk -- out of several, much more fungible, mid programmers.

4) Egos on the line. Tom Smykowski from Office Space is real. Need proof? Propose something sensible like having engineers talk to, or shadow, customers so they can see what the pain points are in their work and which pieces of the software need attention... and see who bristles.

No conspiracy is needed. Companies can remain stubbornly dysfunctional, and will bizarrely spend more to protect that dysfunction, for decades, than to try something better. My father proposed more resilient ways of running an assembly line in the 1960s, upper management laughed. In the 1980s what he proposed was one of the tenets of the "Toyota Way", but it took until the 90s/2000s for American managers to start listening.

This goes double when you realize that software is off most companies' critical path, and addressing performance issues among software teams is way less important than addressing performance issues among, say, sales and marketing teams.


Your comments are describing two different things.

- Technical leaders choosing a language that's easy for developers to Grok to make them more fungible, and make onboarding easier. - Corporate managers purposefully choosing a language as a "Response" to Common Lisp, as you said, to tank the performance of a company for more funding.

One is actually happening, the other requires me to believe corporations even know what Common Lisp is, which would be amazing. You're lucky if management even knows what Python is.

The conspiracy theory I'm referencing is the fact that people think Corporations just hate developer proficiency and will actively fight against tools that make people more proficient. That is different from someone coming to a manager with, from the managers perspective, an idea that could increase efficiency but make them look bad. The first is not something a manager actively does, but the second is an insecure reaction to someone "Rocking the boat."

Common Lisp did not take the world over for lots of reasons beyond hand wringing PMs.


You're missing the point.

It's not really a response to Lisp as such, it's a response to anything which makes developers more productive. Developments from the Lisp world have had trickle-down effects that make other languages and IDEs more powerful: garbage collection, hot-reloading, etc. The productivity gains realized by these developments, however, have been more than neutralized by a concomitant increase in corporate BS. So it's not really a conspiracy of orgs out to get Lisp but it does make Lisp a bad fit for organizations which rely on inertia, ego, and fungible worker-units, which is most of them.


I think the key point was that maintaining control over the employees by making them replaceable has more benefit that better products. That's what market capture is for


> This is a ridiculous LISP conspiracy theory that really needs to die.

Not really. Lisp can be a power amplifier if you can keep it under control. But, as I've argued above, that limits you to a small stable team.

If I could start a new company with a few solid Lisp buddies and I knew that's going to be the core team for the next decade, I think I'd use Lisp and it would be a significant productivity gain.

But but but.. those are some unrealistic constraints. If I'm starting a company presumably I want the company to keep growing as much as possible, not stay with a few core people for years. So no, I wouldn't use Lisp.

> to have too many employees

All of them, the goal of both startups and public companies is to continuously grow. As soon as growth stops that is seen as a problem and the decline begins. The only exception is privately held companies who don't run on VC money and don't have any intention to become public. Those might be the best candidates for Lisp.

> to work slowly

No company specifically wants that, but they vastly prioritize being able to hire 10,000 scrum interchangeable cogs to do development and that's not the environment where Lisp works well.


It's possible to build teams in the low hundreds. Example:

https://www.youtube.com/watch?v=hMVZLo1Ub7M

Siscog, company is 37 years old, 130 employees, 1.7 Million lines of Lisp code. In the planning/scheduling domain for railway companies.

Other example ITA Software, bought by Google for $700 Million dollar. 100+ Lisp developers.


> Siscog

This is really cool! Had not heard about them. Thanks!

Nice, so looks like low hundreds of developers may be possible given the right environment.

That said, I don't feel it disproves my impression. If the company is 37 years old, still privately held and still has less than a hundred developers then it is clearly not a company looking to grow fast above all else. So feels like a nurturing environment for Lisp.


> looking to grow fast above all else

They may also not need to. They may have enough people working in a productive environment. If you add a Lisp feature to a large code base, it may not take a lot of time to do so, given the possibility of an incremental development style with Lisp (-> changing running applications incrementally).


Lisp's primordial sin is the fact that it is FP. US cooperations never supported FP (see Microsoft and F#). FP is largely a European phenomenon.


Lisp is non-opinionated and supports FP, OOP, AOP, in whatever mix you want.


In companies, most languages have some kind of system to enforce style guidelines and restrict which dependencies can be used. Companies already restrict the flexibility of the languages they are already using. So flexibility is not the reason Lisp is unpopular among companies.


> some kind of system to enforce style guidelines

Are you talking about autoformatters like gofmt and its ilk? That won't enforce a consistent approach in Lisp which allows the programmer to infinitely outsmart any tool.

Or if you're talking about written-down guidelines, that can help, for a while. But those morph over time too, and so does the new code but the old code lives on forever. Every time a new CTO/Chief Architect shows up, things change but the existing code doesn't. Since we're on the topic of large companies with decades-old codebases, things have changed many many times and you'll have a very inconsistent mess in your hands. It happens even in fairly rigid languages like Java, I can't imagine what you'd end up with Lisp.

Has there ever even been any Lisp-based company with a 20+ year old codebase where a cast of tens of thousands of developers have worked on it over the years? I can't think of any but maybe I haven't heard of it.

Lisp is awesome for a team of 1 (for about a decade I used to write all my personal use code in Lisp) or maybe a small tighly-knit group. Beyond that, I can't see it working well over the long haul.


I definitely include linters, not just style formatters.

> It happens even in fairly rigid languages like Java, I can't imagine what you'd end up with Lisp.

I imagine it would be about the same, with human factors dominating. If Lisp's flexibility pushed in the direction of inconsistency, the ease with which you could write codemods for Lisp would push in the direction of consistency.

> Has there ever even been any Lisp-based company with a 20+ year old codebase where a cast of tens of thousands of developers have worked on it over the years? I can't think of any but maybe I haven't heard of it.

I haven't heard of any either. If you hear about one, LMK if they're hiring.


While ITA no longer exists as a separate entity I don’t think, my understanding is that they pretty much wrote everything in Lisp.

https://en.wikipedia.org/wiki/ITA_Software

https://franz.com/success/customer_apps/data_mining/itastory...

Grammarly too, although I don’t think it qualifies as a 20-year codebase: https://www.grammarly.com/blog/engineering/running-lisp-in-p...


ITA started doing everything in Lisp, but especially as the original coding founder left, started hiring people doing stuff in Perl, Python, Ruby, Java, shell, C++, PL/SQL, etc.

The core software was still Common Lisp, but lots of cruft got added, and Conway's Law kicked in mightily.


And what's the point of having Lisp if you can't use it? Unpopular opinion alert: Bigger companies want programmers to be replacible resources which means they need to have huge talent pools. If you choose your tech stack for the fact that you can retain your people for only 12 to 18 months, you can't effectively use any of the advantages Lips languages offer to you.


> Simple, not very flexible, everyone does it the same way mostly, you can plug & play developers like scrum demands we do.

I think that's why Python beat Ruby. :p


Agreed. And why I love Ruby and hate Python. But yes.


Ruby is "perl-esque" in syntax, and of course TIMTOWTDI is opposite to the 13th item of the Zen of Python, so it's understandable some dislike to an express design decision.

But hate? Really?


I personally really do hate python.


> But hate? Really?

Coding with Python makes me feel like I'm hand-washing dishes that have been dirtied and left in the sink for several days.

I know that many like Python, and I'm not arguing against that in any way. Whatever floats your boat, mang.

However, trying to tell me why I "ought to like" Python is like telling me why I "ought to like" cilantro. I know you think it's delicious. To me it smells like squashed bugs, and tastes like dish soap.


The dish soap thing is genetic if you didn’t know. The majority of the world doesn’t taste the dish soap.


> But hate? Really?

Yes; well maybe hate it a strong word reserved for life and death matters, but I intensely dislike python. It's like nails on a chalkboard reaction for me. It drives me crazy that it has become popular.


Wow. I feel that way with C# and Java, but mostly without any reasons I can rationally articulate. Meanwhile, I love Python!


> The anti-Lisp is something like Go. Simple, not very flexible, everyone does it the same way mostly, you can plug & play developers like scrum demands we do.

This has not been my experience with Go. The only areas where Go actually enforces consistency is that gofmt has no configuration surface, and Go Modules conclusively won the dependency management war. Other than that, it's still a lawless wasteland.

Foreword: Go is not bad, far from it. It does a lot of things right and you can do a lot worse. But myths around its simplicity, idioms, best practices, etc. have created a generation of monstrous tech debt hiding behind tidy syntax.

One of the worst myths is that it's easy to see the right way to do something in Go, because now when everyone does things their own batshit ways, they each think they did it the obviously right way.

* Go has no build system, and you'll need one for anything but the most trivial projects. Many use `make`, but some people insist that `just`, `ninja`, etc. are worth people having to install extra dependencies. The worst is when projects use straight shell scripts, having all of the platform compatibility problems of make with none of the benefits.

* Go has no macros. Do you use reflection, code generation, or write everything out by hand. You can't even parse some JSON without being forced to make a choice here. (The standard library option, `encoding/json`, is the worst by far. It's also the most popular because of course it is.)

* Even when you're done with that, you still have to choose a way to validate that input, because e.g. Go doesn't even have a concept of a value being "required" so you'll be introducing that somehow. The "validation" frameworks (sigh) that have an opinion on this still do it inconsistently for different builtin types, and often silently do nothing for custom types. Good luck being consistent even within a project let alone between projects and teams.

* Go generics are very limited, and how you work around those limitations can vary greatly, e.g. do you use receiverless methods to simulate associated constants/functions or do you go back to reflection for those. There's still no solution for associated types, you either make do without them or abandon the type system altogether and use reflection. (In a LISP thread it might be hard to imagine just how bad this can get, because there's dynamically typed in a language that's built for it, and there's dynamically typed in a language pretending to be statically typed)

* Channels, mutexes, and atomics all have their place, but most people never understand their tradeoffs properly (and community myths do more harm than good), so not only do they architecture projects around different options, but often the wrong options for their requirements. People will make performance arguments with no measurements, and correctness arguments with no proofs, etc. and when you join an existing project you'll be sifting through its uniquely crafted wreckage to figure out what invariants can even begin to hold.

* The Go community has a lot of vitriol against testing frameworks, but Go has no useful functions for comparing values of non-trivial types, so you either use a test framework after all or reinvent one badly. You can't even define equality in Go recursively through pointer, slice, or map types -- strings compare by contents, pointers compare by address which is almost never useful, and maps and slices cannot be compared at all. It's not even consistent among builtin types. It's very common to use reflection here, see above, this is its own special hell.

* Go has no sum types, pattern matching, or enums. (No, multiple value returns are not enums, they are not a type you can use as a map key, they cannot nest, etc). You'll be reinventing these somehow with structs and interfaces and switches which have to choose to panic or ignore unexpected branches. Many projects, knowingly or not, create unenforceable sum types where different records use different subsets of fields of a struct. It makes C look modern and high-level, because at least C has unions.

I could go on. Go gets far more credit here than it deserves. Again, it does a lot right and it can be used well, but you should not expect quality or consistency from a broad range of programmers just because it's Go. Management that banks heavily on this myth generally pays for it dearly.


> Go gets far more credit here than it deserves.

When I said it is kind of the anti-Lisp, I wasn't thinking of it as credit...


I needed this. Thank you.


> But idiomatic Lisp code really does use linked lists extensively,

Idiomatic python code uses lists extensively. In my (somewhat limited) experience, they're the default data structure to use for a lot of algorithms.

Slightly more accurately, a lot of stuff in python uses sequences extensively, which are implemented by a number of types - but the default sequence is a list. And strings are lists. Yeah, if a list doesn't cut it then try sets or tuples or generators or coroutines or something else that implements a sequence. But for your initial prototype? List.

And python's pretty popular.


A Python list is an ArrayList/vector, not a linked list. CL lists are linked lists of cons cells.


Correct. There's no such thing as a python a-list, or p-list, and they can't share structure. There's overlap between programming in Python with lists, and programming in Lisp with lists, but not as much as it appears.

In idiomatic Lisp, it's rather common to search a list for a value, cdr it, and cons that cdr to the collection you're building up. Python has nothing like that, because what I said makes no sense in terms of Python lists.


What do a-list/p-list have to do with sharing structure?

If you want shared structure of lists in Python, use `itertools.chain`.

If you want key-value mappings, use a dict. Order is preserved by default nowadays, and conversion to/from an iterable of 2-tuples is trivial.


> If you want shared structure of lists in Python, use `itertools.chain`.

That would involve zero shared structure. itertools.chain is just an iterator that iterates one iterable and then, instead of declaring that it's done, goes on to iterate another one.

Shared structure between lists means that two lists refer to some of the same regions of memory.


And multiple `itertools.chain` instances pointing to the same list chunk(s) also use sharing.


OK, that is true, but that approach would have the significant downside that you can't pass over a list more than once.


Iterators can be shared in Python e.g., here's a well-known grouper recipe (traverse `it` iterator in chunks of `N` items each):

     zip(*[it]*N)
Example:

    >>> L = [1, 2, 3, 4]
    >>> *zip(*[iter(L)]*2),
    ((1, 2), (3, 4))
Here's the single result of the iter() call is shared. The purpose is to show that iterator can be shared in principle (ignore other aspects of the code).


Does the CL spec require the implementation to use linked lists or merely to behave as if it were a linked list? I believe it is the latter and that makes a world of difference when it comes to optimization.

A great mainstream example of this are JavaScript Arrays. They are defined to behave as hashtables. They inherit from Object. Their "indices" are actually strings instead of numbers (all object keys are strings and numbers get converted to strings before the lookup happens).

Despite that spec model, JS arrays have used a very different representation in practice for decades. Even in the dark ages, arrays were implemented as linked lists. Today, they might be hashtables, linked lists, boxed objects, or even unboxed primitives all while still pretending to be hashtables.

Cons doesn't have to return a linked list element.


> Does the CL spec require the implementation to use linked lists or merely to behave as if it were a linked list?

"list: a chain of conses in which the car of each cons is an element of the list, and the cdr of each cons is either the next link in the chain or a terminating atom." -- https://www.lispworks.com/documentation/HyperSpec/Body/26_gl...

Interpret that as you will. But what "world of difference" optimizations are you suggesting here? Give an example of a significant optimization at the implementation level that preserves cons semantics.


People confuse code at the exploratory phase with final production code.

Linked lists are a massively flexible data structure which can be great when you are still feeling your way around a problem domain.

Once you have a good sense for the shape of your data it is relatively trivial to go back and update the codebase with performance optimized structures.


I've felt my way around a new problem domains many times, and my experience is that a vector and a map will do just fine.


That may or may not be the case (that they're great for exploratory code, I have actually mixed feelings about that), but it in fact points directly at the problem I was referring to: using Common Lisp in the idiomatic and expected way involves reading and writing a whole bunch of list processing code.

It's not so much that it's a foreign language in the syntactic sense (although it is) but the idiomatic semantics are also pretty foreign to the modern developer. That's a barrier whether or not the promised land is on the far side of the hill.


How is the idiom of linked lists foreign to modern devs?

You create a list of items. You can add/remove elements to the start, end, and even the middle and even indexing is just as easy with the `nth` function.

From a programmer perspective linked lists are universally better. That is to say that if a computer could perform operations on a linked list as quickly and with as little memory as it could for an array, nobody would ever choose to use arrays. We only use arrays because the computer forces us to.


> if a computer could perform operations on a linked list as quickly and with as little memory as it could for an array, nobody would ever choose to use arrays

You can start from a false premise, and draw any conclusion you like. If a computer could perform operations on a linked list as quickly and with as little memory as it could for an array, purple unicorns would be grazing on my lawn right now. :)


My assertion is that linked lists map BETTER to the human and WORSE to the computer. The thought experiment is simply a way to prove that fact and is not a premise let alone a false one.

If arrays and linked lists had the same performance characteristics, we'd pick linked lists every time because they map better to how humans think about problems while arrays map better to how computers think about problems.

Thus we can conclude the exact opposite of the assertion I was responding to. People naturally gravitate toward linked lists then get forcibly re-educated to use arrays because that's what makes the computer happy.


My assertion is that linked lists map BETTER to the human

That's your assertion, but you didn't back it up by anything other than saying you can insert into the middle of a list (which is rare to need and can be done with a sorted map).

Thus we can conclude the exact opposite of the assertion I was responding to

You can't conclude anything from someone making the same assertion with no evidence over and over.

Pragmatically what people use over and over are arrays and hash maps. There's a reason perl, python, lua and javascript all thrived by having arrays and hash maps built in to the core of the language.

If you want to loop through something or index by an offset you use an array. If you want to access by key, you use a hash map. In the rare scenario you need to insert into the middle of a specific order, use a sorted map. Modern programming has no place for basic linked lists of granular data and they aren't missed in any sort of real scenario.

If can back up what you're saying with any sort of evidence or a specific scenario, go ahead.


Linked lists in their basic form are essentially obsolete. Having small items linked by pointers on a modern computer means following the pointer, allocation and deallocation are all more expensive than using data.

This sounds like a backwards rationalization without a technical explanation. Why would a linked list be any better for 'exploratory' programming? The vast majority of data structures are arrays, hash maps and occasionally a sorted map for the times where you need to insert something in a specific place.

What scenario is a linked list valuable in 'exploratory' programming where it wouldn't make more sense to use a different basic data structure?


I love Python's list comprehensions. When I first discovered them I had a kind of "mind blown" moment. They look like this, for anyone who doesn't know:

  squared_div_by_3 = [i**2 for i in range(10) if i % 3 == 0]
Without using a list comprehension, this is equivalent to:

  squared_div_by_3 = []
  for i in range(10):
      if i % 3 == 0:
          squared_div_by_3.append(i**2)


Of course, Python would have been way more awesome if they did not reject the much more reader-friendly and idiomatic:

  squared_div_by_3 = [
      for i in range(10):
          if i % 3 == 0:
              i**2
  ]
That is, just transforming the normal for loop and if into a list comprehension by surrounding it with brackets. It would even preserve the friendly forced blocking and indentation. Can you imagine?


It's inspired by set-builder notation: https://en.wikipedia.org/wiki/Set-builder_notation


Nim has a `collect` macro which works exactly like this.


Reads less like Perl in Lisp:

    (loop for i below 10 when (zerop (mod i 3)) collect (* i i))


With iterate, for comparison:

  (iter (for i below 10) (if (zerop (mod i 3)) (collect (* i i))))


Are you joking?


There seems to have been a design emphasis in C# on providing something similar under the name LINQ. It would look like this:

    var squared_div_by_3 = from i in Enumerable.Range(0,10) where i % 3 == 0 select i*i;
or alternatively:

    var squared_div_by_3 = Enumerable.Range(0,10).Where(i => i % 3 == 0).Select(i => i*i);
In that second syntax, `Where` is exactly equivalent to Python's `filter` (or lisp's `remove-if-not`) and `Select` is of course equivalent to `map`; I assume the odd choice of names for C# is meant to appear similar to SQL.

They're introducing Range objects with syntactic sugar so you can say `0..10` instead of `Enumerable.Range(0,10)`, and you'd think there would be LINQ implementations for those objects, but there aren't.


In Haskell:

let xs = [x*x | x <- [0..], x `mod` 3 == 0]

You can decide later how many you actually want.


In racket:

    (for/list ([i (in-range 10)
               #:when (zero? (remainder i 3))])
       (pow i 2))
there’s also for/vector for/and for/or for/first for/last, most likely for/set. Hashes. can add your own, etc…


I've not used python much but is that not the same/similar to the unfold function in functional languages?


I like python significantly more than lisp.

There are so many ways to express yourself in python that are troublesome in lisp.

I actually think list manipulation is easier in python than lisp.

I don't know, is there a lisp dialect that makes common data structures available in a multitude of ways?

I seem to be able to manipulate lists quite easily in python, and switch back and forth to sets or hashes.

but in lisp you have to dig in to find (for example):

- how to prepend to a list

- how to append to a list

- how to remove an element from a lisp

- how to do slices of lists

it seems to me like a lot of operations in lisp are needlessly efficient too.

Like instead of copying a list, you have to modify the list in place.

if my list will only have 10 elements, and my machine does millions of instructions per second, can't I copy it around a few times if I want?


> is there a lisp dialect that makes common data structures available in a multitude of ways?

Clojure. The language is built around immutable data structures with the expected interface for maps and vectors, and the idiomatic list-churning we're both referring to is unified through the sequence abstraction. So switching from a vector to a map can often be as simple as switching constructors. Because they're immutable, every operation makes a "copy" but does so efficiently.

I don't have a lot of use for Clojure these days but I enjoyed working with it. Rich Hickey is a smart fella.


this is a controversial take, but IMHO, the immutable data structures make Clojurescript (+ Reagent) a very, very nice way to build SPAs.


I don’t think you know Common Lisp very well, or are talking about some super limited Lisp dialect I’ve never heard of.


99% emacs lisp

hmm... maybe I need to look at the packages that use common lisp.


You only have to dig because you are less familiar with the language.

> copy list

Almost all functions have a copying and destructive variant.


This is all "I don't know the language" type stuff.

If you don't know Python, slicing syntax is also strange.

Common Lisp was VERY forward-thinking and has all kinds of these features that only became mainstream decades later.



Look into Hylang.


> And strings are lists

Eh, not really. Lists are mutable, strings are not.

Strings are iterable and sliceable, which makes them usable in many similar ways to lists, but they're not lists.


Afaik, in Clojure lists are immutable by default, and strings (not literal ones) are mutable in Common Lisp. So I'd say depends on the flavour of your Lisp.


> It's the lists.

That is one of the defining differences between Clojure and older Lisps. Clojure has list, vectors, maps, and sets as equally well supported data structures in the syntax and standard library. Classical Lisps often miss proper abstractions over different data structures. Clojure has those as well.


I think it's strange that anyone would even ask why common lisp isn't popular. There is no payoff to writing something in it. The binaries are huge, it is going to be a lot less clear than modern C++, it won't be as fast, you still have a garbage collector, the libraries are niche, the syntax is reversed, the tools are niche, the ecosystem is niche, and everything you do is the opposite of what is intuitive at first.

Then on top of all this is built on linked lists which are essentially an obsolete data structure in their simplest form.

There is no reason to learn something with backwards syntax and ancient tools when there isn't even any payoff. Write something in C and the program is fast, small, native and can be compiled anywhere in a fraction of a second. There is still a payoff for all the very real pain. In lisp there is just no reason to use it from any angle other than how clever someone can be with macros and that is the exact opposite of good sustainable programming.


I don't think it's the lists. I think it's the cons cells. Lists are pretty ergonomic in most languages. As someone who loves Lisp (Scheme, specifically), cons cells are elegant for the compiler nerds and a complete nightmare for the programmer. Fuck `car`, `cdr`, `cdar`, `cddar`, `caar`, etc.. It's a horrible interface and every time I need to remember which variant gets me the value stored at the tail of a list I get a little more angry and want to write more Clojure and less Scheme.


I disagree. I have no problem with lists obsession in other languages like OCaml.

The parentheses are just too much. I get how elegant and unambiguous they are for computers but I am not a computer. It's like RPN. It's elegant and easy for code to parse and unambiguous and all these nice things.... except it isn't easy for me to parse.

Compilers are perfectly capable of compiling readable code like Rust so I don't see why I should have to do the tedious work of figuring out all the parentheses manually.

Lisp is like a really great IR. But I don't want to program in an IR.


> I don't see why I should have to do the tedious work of figuring out all the parentheses manually.

I think most people who write a lot of Lisp don't do that. I use Paredit mode in Emacs, which doesn't allow the parens to become mismatched and has operations like "move the last token out of this expression" and "jump to the next expression" so it actually feels like you're editing a tree rather than a chunk of text.

A quick search says there's an equivalent for VSCode, and I'm sure other editors have options as well. I've seen some amount of structural editing for languages that don't use s-expressions, but it's always pretty limited.

If you've written Lisp with a good structural editor and still don't like editing a program as a tree, then we think about code very differently.


> operations like "move the last token out of this expression" and "jump to the next expression"

And that is the problem. When I am writing code, last thing I want to do is to think about tree manipulation and the numerous commands to do similar-but-not-the-same operations.

This is the same reason I don't like Vim, it's hard enough to keep the problem domain in mind, there's no mental space to also remember the ed-style editing commands.


This must be where we think about code very differently.

Most code has a tree structure, though many put the root token outside the brackets that mark it. Languages like C, Java and Rust use brackets to express their tree structure much like Lisp does, though they use more flavors of bracket than Common Lisp. Python uses indentation, but it's still expressing a tree. Ruby, Lua, BASIC, and others add keywords, but the stuff inside `do ... end` is still a branch of a tree.

Without a structural editor, I'm still building a tree, but I have to keep more of it in my head.


After the learning period it becomes muscle memory and you don't have to think about it. If you're not willing to put in even the small amount of effort to build up new muscle memory, I don't know what to say other than don't be so defeatist.


I think it's as much about familiarity than anything else. I've programmed full time in Clojure for the last 6 years, and I find it just as easy to read than other languages I'm familiar with (JavaScript, Java) and way less easy to read than other languages like Haskell or OCaml that have their own syntax lineage. I'm sure if I spent an amount of time becoming familiar with them, I'd find them just as easy as I do Lisp.

There are certain things that languages can do that make syntax easier; for instance, Clojure's default constructs reduce a lot of parens compared to CL by using brackets [] and removing nesting. For instance

    ;; clojure
    (defn sum-and-square [a b]
      (let [sum (+ a b)]
        (* sum sum)))

    ;; common lisp
    (defun sum-and-squaer (a b)
      (let ((sum (+ a b))
        (* sum sum)))
This leads some people to assert (with other reasons too) that Clojure is not in fact a "lisp."


    ;; common lisp
    (defun sum-and-square (a b &aux (sum (+ a b))
      (* sum sum))


    ;; clojure
    (defn sum-and-square [a b] (let [sum (+ a b)] (* sum sum)))
Honestly, I'm not sure what you're trying to show.


I'm not sure what you are trying to show? Code can be in one line? Okaaaaay...

I was trying to show that one can define local variables without adding another list level via let.

Like if one would/could write in Clojure:

    (defn sum-and-square [a b &blah sum (+ a b)] (* sum sum))
Another Common Lisp example, we'll stick to your one liner format, using infix syntax via a reader macro:

    (defun sum-and-square (a b) #I(sum=a+b,sum*sum))
Before you ask: what am I trying to show? This shows that Common Lisp syntax can be deeply extended by the user and that this is a standard feature of the language. Want a shorter infix notation without s-expressions? Why not...


Reader macros are global and stateful, this is one of the worst things about Common Lisp. Excellent feature, pity we're forever burdened with the implementation.


> It's like RPN

That's genuinely fascinating for me, because I find postfix notation to be more intuitive and easy to parse than infix notation.


It's definitely easier to parse programatically, but IMO it's a nightmare to read as a human. e.g. compare these expressions:

  (x+y)^2 = x^2 + 2xy + y^2
  x y + 2 ^ = x 2 ^ 2 x y * * y 2 ^ + +
I find it nearly impossible to parse this without actually maintaining the stack in my mind.


> I find it nearly impossible to parse this without actually maintaining the stack in my mind.

You don't write out RPN in a long sequence like that. I'm a devoted RPN advocate and seeing "x y + 2 ^ = x 2 ^ 2 x y * * y 2 ^ + +" is indeed difficult to parse. But that's not at all how you use RPN.

You compute the problem going from smaller units to larger units getting intermediate results as you go and combining them. You'd never write out the sequence as you did and try to follow it after the fact.


I went through EE using an HP 49G+ and continue to this day using RPN in Emacs calc-mode.

>> I find it nearly impossible to parse this without actually maintaining the stack in my mind.

> You compute the problem going from smaller units to larger units getting intermediate results as you go and combining them

That's totally it. I've tried doing infix/standard algebraic notation on a calculator and unless the screen is big enough to see all of your brackets you're almost certainly going to screw up one of the big terms in a fraction or something like that. I think a good different way of stating the "intermediate results" part is that instead of computing the solution from left to right you're computing the solution from the inside-out.

By far the most satisfying part of doing computation in RPN is that moment at the end when you've got a big stack full of intermediate results and you just do that "+ + + + +" to add them all up and bam! Answer!

Edit: I had two thoughts back-to-back after writing that.

First thought: I do actually struggle sometimes with doing math in Lisp because prefix notation isn't a way that I'm used to doing math. Which led to "Hmmm... what if you did Lisp but with postfix notation instead of prefix notation"

Second though: "Ohhh... that's Forth. And you don't need brackets at all..."


> Ohhh... that's Forth. And you don't need brackets at all...

You still need them if you want variadic functions. Symmetrically, you don't need parentheses in lisp if you don't want variadic functions, as long as you're willing to draw an explicit distinction between calling a function and referring to it.

You might as well just make that distinction by using the parentheses, though.


I'm convinced a large part of the benefit people see from RPN calculators is that you have to figure out the order of operations before you start. It forces a way of thinking about the equation which is less error-prone, you're more likely to notice mistakes as well.

Takes a little getting used to, but the interesting thing is that analyzing an equation for the proper order of operations on an RPN calculator is not at all an error-prone process. It's a careful process, and it forces an understanding of the equation itself which just churning through it on an infix calculator does not.


How would you write this algebraic identity?


You write equations in the usual algebraic form. You don't write RPN on paper (or screen), that's not a thing.

When you're going to compute a value on the calculator, that's where you use RPN starting from the innermost computations working outwards, because you have a stack to keep all the intermediate results handy.

Again for like 2+2 it doesn't make any difference, but if you have a long formula with lots of values and different operations, it is much easier to do correctly and fast with RPN.


But this was a super easy sequence.

Can you give an example how it should be written?


It's exactly how you would do the calculation if you ran it by hand, unlike the regular algebraic notation. I am flabbergasted that anyone uses the latter, especially as for most of the history of mathematics calculation was done by hand!


I don't "run" x 2 ^ by hand and I don't think anyone does. Some people find different notations more intuitive and that's fine but whether it's prefix postfix or infix, it's still an abstraction.


> I don't "run" x 2 ^ by hand and I don't think anyone does

Of course everyone evaluates expressions symbolically: in code review, when debugging, even when looking over an expression just after typing it to make sure it’s right.


Many languages have different verb order - English is notionally SVO (subject-verb-object), while Hindi is SOV.

That is probably much more related to why we use prefix/infix/postfix notation: because it maps to our native languages better.


For simple stuff, sure. Try -b±√(b²-4ac))/2a. Or anything complicated really. You end up counting parenthesis, writing down intermediate values, and just blowing it and starting over.

It's really nice to have something complicated, being able to sanity check intermediate values, and having a visible stack made things crazy easier.

I had friends in high school, for anything non trivial they would call out "anyone get 123.5 for the answer?". Look at me, and if they didn't get my answer they would try again.

So sure, postfix is easier for the simple stuff (which is easy either way), but postfix is MUCH easier for anything non-trivial.


> For simple stuff, sure. Try -b±√(b²-4ac))/2a. Or anything complicated really.

In anything complicated and long is where RPN shines the brightest!

If it's 2+2 I don't care whether I type 2+2 or 2 2 +. But for long complex equations, give me RPN every time. All my calculators are set to RPN mode (if it doesn't have RPN mode, I'm not using it).

I know it takes a bit of getting used to, but once you do it is a huge advantage. Particularly great for university tests where speed matters.


Indeed, great for speed and correctness.

It also makes awesome use of a multiline display, even an extra 3 lines to see the stack is a game changer. Without RPN I just use the extra area for graphing.

It's especially useful for unit conversions, since with prefix you never see the intermediate values.


With Rust do you close all your } manually? Do you have an example of Rust code where the equivalent Lisp code would be harder to parse, assuming equal familiarity with both languages?


> With Rust do you close all your } manually?

You don't have to, because you don't end up with }}}}}}}}} in Rust, because statements end with ; and not }.


I didn't end up with ))))))))) in Scheme because it was easy to create smaller functions. I do see rust ending with

      }
    }
  }
In TypeScript I'll often find myself ending blocks of code with

        });
      }
    });
  }
I'm having fun with TS, but I do miss the parentheses. They were simpler.


1. Lots of small functions lead to code in which bugs often hide in plain sight, see this old memo by John Carmack: http://number-none.com/blow/john_carmack_on_inlined_code.htm...

2. Scheme is better than Common Lisp when it comes to Lots of Irritating Superfluous Parenthesis, since declaring a variable in Scheme (using 'define') doesn't create a new nesting like it does in Common Lisp (using 'let').


I do, but there are about 4x fewer }'s in Rust than there are )'s in Lisp.

Look at these:

https://rosettacode.org/wiki/Archimedean_spiral#Common_Lisp

https://rosettacode.org/wiki/Archimedean_spiral#Rust


I'm tackling a significant lisp project right now, and the thing that holds me up right now is that the code is difficult to organize. Python, java, rust, go etc have well-defined patterns to figure out where code lives and where you might expect certain behaviors to occur. With lisp you can really shoot yourself in the foot very easily by using abstractions that are difficult to follow and are spread out across many files.


I like to compose programs from small one purpose Common Lisp libraries, each is a Quicklisp project. I used to write larger monolith CL projects, but I like my newer approach better. Off topic, but I take the same approach for my personal Python and Racket projects also.


> the code is difficult to organize

we can simply do `(defpackage :mypackage (:use :cl))` then `(in-package :mypackage)`. Since they don't have to follow file hierarchy, we are free.

That's good for the long term, and general flexibility. But it's different, sure. Good luck!


I mean, separating files (or modules or libraries or whatever) seems somewhat orthogonal to my point—where you expect to find behavior, and what behavior you expect, rather depends on how you use lisp.


If there's one language in existence where you can shoot yourself in the foot using abstractions it's Java. It produces spider webs of dependencies, hierarchies, etc.


Right, but that's predictable and straightforward to manage. Lisp is far less structured, meaning there are far fewer guidelines or clear indications of intent built into the organization of the program. Or at least, there's enough complexity it's much more difficult to internalize than Java, even with all its verbose warts.


Lately, I've been creating protocols that are meant to be the interface for some kind of entity, such as "user", "utility", "configuration", "auth", and so on. The logistics of that are to use defgeneric for the protocol interface, defmethod in the same package, but can be over-ridden with a more specific defmethod in another package if necessary. I still export regular functions too, but I look to the defgenerics as the main entry points for a particular package. To (sort of) enforce some separation, I try to keep these packages self-contained in their own system (defsystem).

I mean, it's one approach, but my advice would be to take some time to come up with what would work for your appication and CL is flexible enough it can probably support it.


I'm trying to understand why lists matter. Yes, there are some oddities where some base constructs don't play as well with hashmaps as they do with lists, but it is far from difficult to work with a hashmap. Or an array. What difficulty are you talking about, specifically? (Genuine question.)

Biggest difference I know of, off the top of my head, is the LOOP macro having constructs that care which one you have. Curious if there is more.


O(1) access to the nth item turns out to be more important than O(1) insertion.


You'd be surprised how many exponential loops are in successfully run stacks. :)

But yes, when it matters, it matters. Luckily, it isn't hard to use appropriate data structures. Also luckily, a surprising amount of code will linearly process data.


More specifically, the linked lists are too inefficient for modern computing to be the default data structure. Not enough locality, too much jumping around memory. Reality is a tree, but to organize it, you have to convert it into arrays, maps, and matrix multiplication.


Reality is a graph. We understand it as a multi-tree. But we write it in lists.


The reality of a computer is an array since memory is sequential addresses and programs deal with data in memory.


It's funny that behind the scenes in all languages, everything is lists (in the AST).

It's strange that we tend to initially code in easily readable built-in control structures (if, switch, for, while), but then there always comes a point when you need to refactor...and convert them into lists of data structures and process those.

For example, if you are matching routes in a web server, you can write a bunch of if-statements. Very simple. Easily understandable. And you can use whatever criteria you want, in whatever order you want.

    if (request.pathname == '/foo') { return foo }
    if (request.pathname == '/bar') { return bar }
But say now you want to print a list of all the routes and how they are matched.

Most people create a concept of a `Route` object that contains a predicate, and then you loop over those in a list. It feels super clean. But now if you want an exotic way of matching a particular route, or you want route priority or anything like that, and your route matcher and Route objects start becoming really complex...but if you did it with a simple code block with if statements, it would have been really easy.

    const routes = [ 
      {pathname: '/foo', action: () => {}}, 
      {pathname: '/bar', action: () => {}}, 
    ]
    for (const route in routes) {
      if (route.pathname === request.pathname) { return route.action }
    }
If we could have referenced our if-statement code blocks as a list (using Reflection or something), then we could have avoided any abstraction and stayed totally flexible.

I could easily throw a few more requirements at you, and you would quickly have some frankenstein Route object and matching logic.

It's this weird process of "dont-repeat-yourself" where everything looks the same and you abstract it, and then you realize its not all the same, and instead of back-tracking the data structure, you just tack on new stuff and more complicated logic.

Complexity in software stems from these pre-mature abstractions.


It's not true that everything is a list in most languages. Typically an AST is built with both lists and structs. Special forms are always going to exist and the code needs to deal with them, so this isn't a loss; it's being more explicit. Well-named fields are better documentation and faster than accessing the nth item than a linked list. (You can emulate named fields using just lists, but it's just a convention.)

Also, in most languages, the lists aren't linked lists. They're typically array-backed.

JSON has both lists and objects (which serve as structs) and it's quite popular and usable for representing AST's.


Yes but in whatever language you're imagining here, a "list" probably isn't the kind of list that lisp uses (linked lists of cons cells).


It's vector and hashmap that you need most, not linked list.


I had the same thought about conditions a few days ago and discovered this:

https://discourse.julialang.org/t/ann-package-conditions-jl/...


Lol. The final domino falls!



There isn't a language where you can't find a ragequit file. The Common Lisp ones are absolutely savage.


You are right :) This file is primarily for the ones who rage quit CL, hoping for greener grass elsewhere (and eventually come back).


+1 for writing “processing lists” with a lisp


Common Lisp once had plenty of backing from major companies like Symbolics, Xerox, and Texas Instruments, as well as smaller companies such as Harlequin. Even Apple owned Macintosh Common Lisp at one point, and SK8 (which could be considered a follow-up to HyperCard) was implemented in it. Had Apple not had its problems in the 1990s, one potential alternate version of Apple I could imagine would be some sort of Lisp OS with a Mac interface (true story: the Newton could’ve had a Lisp OS if it weren’t for C++ advocates winning out). Unfortunately, the AI winter of the late 1980s and 1990s severely damaged the Lisp market; companies either abandoned Lisp or went under. The torch of Common Lisp has been carried on by open source and commercial implementations, but there are no major companies actively backing Lisp in the way that Java is backed by Sun/Oracle and that Python is core infrastructure at countless companies, large and small.

I didn’t know that conferences could potentially generate a lot of revenue to help fund programming language development. I’m quite intrigued by this idea, actually. Thanks to advocacy of the language, there are probably thousands of Common Lisp developers in the world, many of whom are working on interesting projects. Perhaps a series of profitable Common Lisp conferences will provide the spark needed to come up with a modern Common Lisp foundation that is able to support further development of the language.


> ...one potential alternate version of Apple I could imagine would be some sort of Lisp OS with a Mac interface...Unfortunately, the AI winter of the late 1980s and 1990s severely damaged the Lisp market...

Your order is messed up: work on Dylan commenced years after that AI Winter had begun.


Yeah, I should’ve clarified regarding Apple, which wasn’t in the Lisp workstation market. Apple’s departure from Lisp has little to do with the AI winter (though Hacker News commenters lispm and mikelevins may have a lot more insight given their expertise and the fact that the latter worked on Lisp projects at Apple). Rather, I believe Apple’s departure has to do with Steve Jobs’ return. Apple was an unfocused beacon of innovation in the 1990s, with many competing visions for the future of the Mac and the company. When Steve Jobs returned, the vision became unified under him: the technical underpinnings of the Mac were based on NeXT technology, and other competing visions (such as OpenDoc) were discarded.


The saddest thing to me is that Brendan Eich didn't do a scheme like he intended.

If that had happened, the web would have been fast 15 years earlier. Things like Flash wouldn't have ever happened. ES2015 would have never been necessary. HTML would have gone away. CSS would have never existed. Declarative web frameworks would have become a reality decades ago. WASM wouldn't have happened. Even stuff like the App Store likely wouldn't have happened because Scheme would have been fast enough that Jobs would have stuck with his initial web app idea.


I don't agree with this counterfactual, much as I love Scheme among languages I've barely used in my career.

Chez Scheme was fast but would not fit in the browser especially on Windows 3.1, which still had the largest share when I started JS in 1995, if memory serves -- it was certainly important to Netscape because Microsoft started bundling IE into later Windows versions, as default browser in Windows 98. Petit Chez Scheme was not fast, and I'm not sure I would have had time to use it in those ten days (I never looked at the code).

Guile was out because of the GPL, not my choice (same as with Make It Look Like Java order) and probably no time to embed, or just no way with 64K segments.

If you know of another fast-in-1995 Scheme that might have fit, links welcome.


I guess I'll start by saying that JS is my absolute favorite among all the top languages out there (StandardML, Rust, Scheme, and CL probably being the others in my top 5). Thanks for making it!

Of course, everything said is hypothetical as we can't actually wind back the clock.

You could have rolled your own Scheme instead of adopting a then-fast implementation. The Scheme would certainly have been faster from the very start if for no other reasons than integers are faster than floats and real arrays are faster than hashtables.

Scheme would likely have been a bit faster from the start, but the real question would be the trajectory from there. JS needs inline caches and hidden classes to be fast. I don't see how all that could have possibly run on typical systems of the 90s. In contrast, there's a lot of Scheme optimizations that would work perfectly well on those machines before resorting to the really heavyweight stuff.

Considering that JS with many millions in investment is generally slower in typical code than something like Gambit or Chez (both of which have very little financial backing) leaves me assuming that the Scheme trajectory would always be better.

This just leaves the hypotheticals of Scheme's other effects.

Anyone who's used basically any lisp for web work would understand why HTML would become effectively subsumed. S-expressions make dynamic manipulation of the raw trees very easy as that's what a lisp is designed to do. React revolutionized frontend development in 2013, but a huge part of what people like most about it would have been obvious to web developers years earlier.

XML may well have never happened either. While JSON was apparently only obvious in hindsight, the idea of sending S-exp over the wire then parsing out the data is very in-your-face with lisps.

CSS is a much more simple matter. One of the major syntax proposals used S-expressions and their easy integration into Scheme would have all but guaranteed their adoption.

WASM likely wouldn't be needed because as has been shown with Common Lisp, you can already get very low level with just lisp (and even lower if you allow stuff to optionally handle its own memory). Asm.js got really complicated to the point that WASM made more sense, but with performance already being very good, it becomes a lot less necessary (even without those lower-level changes).

Flash was a bit hyperbolic. It would probably still have existed for a variety of reasons, but probably wouldn't have become a separate language and ironically would probably still exist today as a development layer over the browser's features.

Steve Jobs was very open that he didn't want an app store, but instead wanted web applications. There was an issue with the browser's capabilities, but the biggest issue was that JS engines were still just too slow in the late 2000s (part of what killed off the very amazing webOS). Scheme would likely have been quite fast by that point making the web app argument much more appealing and possibly saving the world from fractured ecosystems.

Of course, there's the counter-argument that the world would never accept a scheme and it would have either been replaced or moved everyone into the nightmare of Java applets. There are certainly a lot of alternative universes where the world would be far worse if we'd wound up with Scheme rather than Javascript.


It's fun to argue what-if's, up to a point. But I see a couple of factual problems:

1. JS via asm.js paved the way for WebAssembly, and asm.js (https://asmjs.org/) like Wasm is statically typed. Scheme is dynamic. So there would have to be an asm.scm or similar evolutionary path, in order to get to something like Wasm even if in addition to Scheme rather than JS, for the needed win.

Polymorphic inline caching etc. is not enough to compete with native, e.g., to run Unreal Engine 5:

https://x.com/spatialweeb/status/1757581115609817236

Static typing matters beyond raw CPU perf, in order to prove memory safety with definite allocation -- no touching the GC or 30fps gameplay falls over.

2. Wasm is binary, and this matters too: while transport compression helps JS on the wire, or under the alternative, would help Scheme on the wire, the memory cost (plus CPU of LZ) blowing out the compressed payload into source form for parsing is too much for mobile, or for any head to head competition with the likes of PNaCl (which ~2012 era Google was flogging in vain -- no way it would get into Safari or other browsers).

Yes, binary syntaxes for Lisps exist, but it's not the case that everything can be done "with just lisp".

Please use factual yardsticks and apples-to-apples even for counterfactuals that would have to survive in the same ecosystem.


> JS is my absolute favorite

One more true believer:

  >> trueJSBelievers = 100000000
  100000000
  >> trueJSBelievers += true
  100000001


> integers are faster than floats

Bignums are not faster in general and more work, but yeah: optimizing float (or bignum) to machine int is a win. This was one of the first type specializations done in the JS JIT wars of 2008.


None of these "programming language evolutionary fitness retrospective" discussions ever manage to address the real heart(s) of the question:

- UNIX is C, so C-family languages won.

- The network effect is by far the most dominant force in the matter.

All of us would like to believe that we operate in a world where technical merit determines memetic fitness, because our jobs seem to depend on it: after all, if we build good things, we immediately see the consequences, and others must also see and appreciate these consequences, right? By doing well at that, we succeed, right?

Well, it turns out it's not so much like that actually.


MacOS, with a slight help from MS, very, very nearly tipped the balance to Pascal in the 80s. I dare say that without C++ hitting mainstream around 1990, C would have languished.


Now you are rewriting history :)

Turbo Pascal was responsible for the Pascal success. And then came delphi along and somehow lost against Visual Basic.


Early Mac Systems were written in pascal, well, Clascal as they called their Object Pascal system in those days, Clascal was developed internally at Apple with Wirth as a consultant. Think Pascal was the primary alternative to MPW until mid System 6 era. MacApp remained Apple's primary API until the mid 90s, and it was 100% written in Object Pascal (formerly Clascal).

Some chunks of Windows were also written in Pascal, using Microsoft Pascal which they'd been using since their CP/M days.


They were not. The early Mac operating system and Toolbox were written in assembly, with a Pascal API. MacApp was ported to C++ in the late 1980s and went C++ only in the very early 1990s, about 1991-2 with MacApp 3.

MacApp was the framework Apple wrote and promoted, but it wasn’t the primary API; lots of developers wrote directly for the Mac Toolbox or used any of a number of other frameworks like THINK Class Library.


This is very limited to mac, which was really tiny during that period.

The rest of the world looked very differently at that time, and compared to mac had much better development environment.


I admit that I haven't touched CL in decades and my most recent exposure to Lisp-ish stuff is writing a bit of Scheme and reading a bit of Clojure (maybe some elisp too).

I like a lot of the ideas behind Lisp-like languages but I dislike writing in them. Parenthesis, as others mention, start to get unwieldy. I often feel like I'm expressing myself "in reverse", almost like I'm programming in Yoda speak. I mean by that, it often feels like I want to edit/add-to the beginning of a statement rather than the ending of it.

Perhaps the discomfort would dissipate with use. But to be honest, I'd rather just grab Python or even Typescript these days for the kind of ease and flexibility where raw performance doesn't matter. And there are other programming languages that excite me more than Lisp (e.g. Elixir, Unison) so my limited free time has plenty of outlets on that front.

What surprises me is that Lisp evangelists have this default assumption that their language ought to be entitled to popularity. They have some kind of "if people only knew" mentality, just like in this article, where they assume that the only reason people don't flock to their language is ignorance. Perhaps it doesn't occur to them that people try it out and then leave and never come back.


>Perhaps it doesn't occur to them that people try it out and then leave and never come back.

Once the code is data is code clicks there’s no going back. When you read other languages you just see a lisp DSL, you can’t unsee it because you’ve changed.


Isn't this the exact "if people only knew" attitude he mentioned?


Yes it is. If people stuck with it until crossing ‘the veil of ignorance’ there would be no going back.


Just gotta double down eh?


Yes, unironically.


Because most programming is done by people trying to do a job and it requires broad tooling support and familiarity, LISP is a language that has neither.

LISP, (similarly to Haskell) requires you to bend your mind and pay an upfront mental cost in order to access it's benefits, which are that everything is equally easy to describe. No construct in LISP feels like it requires you to bend the language in an awful way because LISP is a fantastically generic tool, some things that are common and easy in other languages are more difficult in LISP, but as you get into the weeds building more complex constructs it will never get in your way.

(and since i mentioned it, the benefit of Haskell is that you can make a LOT of statements about your code you know to be true)


I didn't find Lisp to be particularly costly in terms of mental energy to become competent in. Rust, however, was a heavy lift.

Perhaps different people are inherently more or less compatible with different languages?


I feel like Rust is easy; it's just responsible, 21st century C code with more data structures in the standard library.

Functional languages are like being forced to fry an egg while on LSD. Eventually, you get the hang of it, and you start seeing Spinoza's God in the form constants, but holy shit was it difficult! It's a totally disorienting and mind-bending challenge of re-learning how to do the most basic things!


I find Rust so onerous, I gave it a fair shot, and I see it's merits but I just don't find myself in situations where Rust feels like the right fit very often. Where the cost of it feels worth the benefit. Writing C is and always has been an absolute joy for me and almost everything I'd want to do in Rust I'd rather tackle in C with a big smile on my face.


> Perhaps different people are inherently more or less compatible with different languages?

I think this is true. Peoples' minds work differently; different languages fit different minds better.


When I learned emacs lisp back in the mid-80s, I didn't find any requirement to bend my mind or pay any upfront cost. It was a little different from C (and BASIC), certainly, but once I grasped a few basic concepts, it felt extremely elegant and very well suited to the task(s) to which I was putting it.

More recently, I've felt that way about Python, and about a few chunks of my favorite blobs of self-written C++.


the upfront mental cost debate is still going, a few people tried teachings lisps as first languages and people didn't struggle

i personally cried a few tears when learning java, whereas the weird drscheme class made me all calm and happy

programming also blends a few layers into one, and some people are operating at one (line by line modification of data), some want generic infinite freedom[0], you can rapidly see who will enjoy what there

[0] one trauma i got during studies is failing an exam because after reviewing ada's manual twice, I couldn't find the page explaining the `object'field` syntax and failed not being able to split a string. i never liked ad-hoc syntax much...


For my comparative programming class in college, I was the only person who got all the Lisp assignments done on time and correctly (it helped that I was deeply into TeX at the time), so there is definitely a mind-frame to be in, and some sort of hurdle which a fair percentage of folks find difficult.

One big problem w/ Lisp is that it is so difficult to distribute programs written using it --- I'd give a lot for the ability to compile to a stand-alone program which could then be distributed.


SBCL has save-lisp-and-die. The binaries are big though. LispWorks will give you smaller binaries, but it costs money.


My web app with dozens of dependencies is ±35MB, using SBCL core compression. So that's heavier than Rust, lighter than Deno, maybe in par with Go when the application grows. It starts up in 0.4s, without compression it does in 0.01s.

https://lispcookbook.github.io/cl-cookbook/scripting.html#bu...


When you build SBCL from source, enable the compressed heaps option. Then when you create an app with save-lisp-and-die, you get a fairly small app even without having a treeshaker to remove unused code.


Yeah that's fair, it also came naturally to me but anecdotally I've found a lot of people struggle with it, especially those who came up through a "get shit done first" type python or java pipeline in a traditional institution or bootcamp rather than having a true CS background.


python I understand, but get shit done and Java is not a given :p (partly joking, I understand the platform is industry grade)


Logo was a great teaching language. If you're not already patterned into C-style programming, it is a great experience.


Rust requires people to learn how to adapt to Rust’s borrow checker, which can be challenging for programmers coming from other languages, yet this hasn’t stopped Rust’s rapid adoption in systems programming. In fact, Rust’s safety features are Rust’s selling point.

Of course, it helps that Rust came from Mozilla and that it’s used in Firefox. Other than Grammarly and some internal Google products, I don’t know any modern-day high-profile projects written in Common Lisp. What would help bolster Common Lisp would be a high-profile project that would be much more difficult to implement in other languages.


I find that borrow checked in Rust is the easiest part of the language to get used to. Lifetimes on the other hand seem unnecessary in 99% cases where compiler forces you to use them - why can't it just assume that all the required parts have the 'a and complain if something violates that? Event bigger issue is all the missing features that most other languages have - overloaded functions (they kind of exist in generics), ranged integers, arrays indexed by enums, full support for type aliases, some level of inheritance, even if it was the go style, would make Rust much much easier to use.


> Lifetimes on the other hand seem unnecessary in 99% cases where compiler forces you to use them - why can't it just assume that all the required parts have the 'a and complain if something violates that?

Rust does that, this is lifetime elision. If the compiler complains about missing lifetime annotations, that means it can't infer the lifetime. Granted that it not-infrequently feels like it should be able to, and there's definitely room for improvement on its lifetime inference. It's conservative, meaning that an elided lifetime will always be valid, but some lifetimes which could be elided get missed by inference and need to be added anyway.

But most of the times that I've yelled at the compiler for complaining about missing lifetimes, it was right and I was wrong.


To the contrary I ran into cases pretty quickly that rust just wasn't useful for in my opinion.

One of my use cases was something like a mutable borrow of an struct in a struct in a single threaded code path wasn't allowed because the entire struct was mutably borrowed or some nonsense.

I believe the "idiomatic" way was to create some weird abstraction to appease the borrow checker that the thing being borrowed once is only being borrowed once.

I rewrote the code in C++ and never looked back.


Forbidding multiple mutable borrows is how Rust is able to prevent you from writing code with bugs like iterator invalidation. Has nothing to do with threading.


From what I have observed, most advanced Rust authors don't bother with lifetimes at all. Every pointer becomes an integer index into some slice and they mostly skip the lifetime/borrow checking. Lifetime are only kept on the few, easy-to-reason about cases. Kind of funny actually.


I'm not sure if Rust is a good comparison though. Rust is a lot closer to C, it is a language influenced by C, and current popular programming languages tend to also be influenced by C. So while there is a lot different with Rust, a lot of knowledge from, lets say, a C developer can carry over.

Say your a C developer, or C++ developer and I am a Rust developer trying to sell you on Rust. You have to learn a new language. No way around it. Even though people may have problems with this statement, at a high level it is close enough, I can sell you on Rust probably by saying, 'it C/C++ with better (subjective) or more modern features and extra constraints (think borrow checker) to guarantee memory safety.' I would not be able to make that same argument with LISP.

The next question could be why this could be true, or more importantly, why are so many people using C-like languages. I'd probably place it on portability and UNIX from when C emerged. And LISP machines died.

In other words, I don't think it would be appropriate to try to draw a comparison between the two with the two of them in a vacuum. If LISP machines survived and were popular today, we could see Rust today having the same popularity of LISP today.


I'd argue Lisps are worse today. They encourage a thing that's generally discouraged in C: macro programming and typedefs. You're coding in Lisp, but you're actually coding in your own dialect of Lisp, eventually, with lots of idiosyncratic code that's difficult to reason about. That doesn't scale well in collaborative environments, where many people are touching a vast codebase (which I don't think was really a thing back in the '70s).

That's when you want the uniformity and clarity of static type declarations and C++'s <algorithm> header functions. With Rust, you're just adding modernity and safety on top of that. Great! Lisp is crazy lone wizard shit.


sorry but nah, nobody encourages anyone, let alone newcomers, to write macros. You don't even need to. You can chain function calls and get the job done.

BTW you get many static type checks at compilation with SBCL, instantly, and see Coalton for more (Haskell-like on top of CL).


I think a domain-specific dialect is the best way to unlock the potential of a team of handpicked experts, but a lot of companies seem to hire an army of devs they don’t trust enough.


You can code your own dialect of ANYTHING. OOP does this all the time (anyone for Enterprise Fizzbuzz).

C considers macros as bad because they are dangerous and have all kinds of weird side effects that break things. If they had all the capabilities of lisp macros, you wouldn't be hearing all the complaints.


That is the biggest problem I have with LISP which is why I think a lot of it is historical to some degree. I do like LISP to dabble in, but yea, there are tons of different dialects, it makes it harder to approach. Because while there is 'Common' LISP, there are implementations that can vary slightly between compilers, which compiler do you use? And some of the more popular compilers are sort of dead? There is SBCL, which does get some development, but not much I think? I don't think newer version of SBCL run on most of the BSDs? And yea, you also end up creating your own dialect in the process.

Which some of those same problems could have occurred in C at some point, but C eventually converged. I can be confident my fairly simple C programs will compile just fine whether I use GCC/Clang/TCC, etc. If LISP could have converged like C did, it could be a completely different story, I wonder.


> There is SBCL, which does get some development, but not much I think?

Quoth the website*:

> New SBCL versions are usually released at the end of each month

Also, the only BSD which has even in the single digits of market share, Darwin, is only 1 point release behind.

* <https://sbcl.org/all-news.html>


Half of the phones in the US run BSD. Good luck getting SBCL on it though.


SBCL is a Common Lisp implementation with:

* a runtime in-memory native code compiler (-> COMPILE)

* a file native code compiler (-> COMPILE-FILE)

* and a runtime native code loader (-> LOAD)

Apple forbids providing compiled languages on (I'm talking about "ON THE DEVICE") iOS. Good luck getting those onto iOS. Their own Javascript runtime JIT is allowed, others are not. Common Lisp runtime native compilers are not. Thus one can't move a development Lisp, which includes (!) a native compiler, onto an iOS device and bring that into the Apple iOS/iPadOS/... Appstore. The Lisps which run on iOS (ECL, LispWorks), are compiled outside of iOS. They may include some kind of interpreter (source or bytecode), without a native code compiler component.

Thus: Other languages are not allowed (by Apple) to compile code to native code on (!) an iOS device. On can compile code outside of iOS and move the code to iOS (as a developer). But one can't use a language which compiles to native code ON the device.

There are other restrictions. LispWorks for example runs on iOS, but provides a special runtime with less capabilities than on macOS, also with a different garbage collector. Without the runtime native code compiler.

There might be some BSD in iOS, but different & less form an actually BSD.

To bring SBCL to iOS one thus would need to remove runtime native code compilation/loading and provide a specialized runtime with a different garbage collector. Then this would be needed to be integrated into some other application as a library, or one would need to implement a new GUI for it...


> Good luck getting those onto iOS.

That's what I said, yes.

Although as long as you're not distributing the code these restrictions don't apply. Depending on what you're doing that's either a technicality or that's how you intend to proceed, there's a reasonable amount of iOS code out there which pays no attention to Apple's rules whatsoever, and which you can't download binaries from the App Store of.


The 'BSD' on phones would be iOS and tons of languages don't mostly due to the platform.

I think a better example would be, the amount of embedded/appliances that ship forks of FreeBSD, OpenBSD, or NetBSD. Think of like routers. While Linux is the 1,000 pound gorilla in server space, there is still A LOT of BSD servers also. The person dimising BSD usage is only taking into account desktop share not realizing some OSs run on more than just consumer devices.


I agree with you about the upfront costs. As a functional programming fan (F# in particular), I can’t deny that it sometimes feels like puzzle solving. Fun, but not easy at first.

On the other hand, you can quickly throw together some crappy Python code to do the same thing, but you end up paying 10x the cost over the long run in debugging and rewriting.

So my advice for people who care about the code they write is to pay the upfront cost to do it well. The good news is that the learning curve does get less steep eventually, and at that point you feel like a goddamn ninja.


Python != Crappy code. Lisp != good code. You can write crappy code in any language. My experience is that it is much less expensive to maintain Python in a team environment in the long run. People aren't tempted to create their own personal "domain specific languages" in Python. Lisp kind of encourages that.


I'm not saying Python is always crappy. It's just easier to create crappy code in Python, due to dynamic typing, rampant side effects, null values, OO madness, etc. If your team has the discipline not to do that, great.


> dynamic typing, rampant side effects, null values

I’m afraid these are also problems in Common Lisp.


much, much, much less than Python. CL compilers (looking at SBCL) get you many type checks, at compile time, instantly (you compile the function at point), or you can send computation to compile time yourself, and the OO is different, etc.


Yeah, I’m not a Lisp fan myself. I was comparing Python to functional languages, like Haskell or F#.


This is only true in terms of "free" options. There were (and are?) decent paid options that had quite good tooling. In the free software realm, though, it was lacking.

And... this is kind of the point of the post. The python community doesn't just push to advance only the language, they advance the use of the language in the free software world. What is the closest that the common lisp world has?


Package management kind of sucks, the ecosystem sucks, other languages continually improve but nobody will ever advance the CL standard. There is nothing compelling about the language to people who aren't already Lisp people.


> There is nothing compelling about the language to people who aren't already Lisp people.

CL is expression based (like Rust, unlike other mainstream languages I've seen), has a concise macro system (more convenient than Rust's imo), has a GC (simpler to use than languages with manual memory management or RC-only), has a better developed ecosystem then some new languages (ex. automatic ffi generation; while buggy, tremendously helpful compared to writing bindings manually). And it's not pure as in Haskell. And it has type annotations that may be checked at runtime or improve performance. An implementation like SICL could make it viable to use it as a scripting language.

Any similar modern languages with better tooling/ecosystem? Perhaps Julia, haven't seen it yet.


For package management, we have:

- Quicklisp, which is pretty handy, since we can install and use a library from the Lisp REPL, without restarting anything.

- Qlot, that installs dependencies locally, and allows to pin them (yeah you can't do that with QL, although cloning a lib and using it is easy).

- ocicl, a new package manager from the world of containers

- CLPM, another new one

- Roswell, if you really really want to install libraries from the terminal (although Qlot does it) or to install implementations, or software.

I bet the ecosystem is bigger than many think: https://github.com/CodyReichert/awesome-cl and anyone would be amazed by its stability.


Package documentation which seems to mostly consist of a list of variables and function names, possibly with a single sentence describing what the function does. Possibly.


it's paradoxical since lisp people weren't born this way, surely there was some appeal (beyond being influenced by teachings) ;)


I do think there are some traits like curiosity, open mindedness, and patience that make some people more susceptible to infection.


> nobody will ever advance the CL standard

And that’s a good thing.


I suppose, if you are happy with the state of things and the size of the current userbase.


I can’t imagine a single feature that would be worth a change, and I have plenty of complaints about the library.

I also can’t see how that would make it more popular.


I'll never understand why a language needs to constantly evolve to be popular. There's nothing fun about having to keep up with changes, and continuous development just screams to me that a project has not yet matured. When something is stable enough to just keep working for 10 years without an update, that's not a bad thing. When did that become the mindset?


I love programming in common lisp. It has made programming fun again.

I don't know if that makes me a lisp person. I just know it makes programming fun for me.


I'm curious if you can put your finger on exactly why you find it fun?

(While I've never used CL, I do use Clojure and similarly find it huge amounts of fun.)


Because I can eliminate drudgery in a way I can understand.

I find that no matter what language I use, I eventually want code that writes code. Other languages have bad tools for this. Sometimes I use an editor, or something else like m4, to generate code.

Haskell has Template Haskell, which is icky in a variety of ways, and it has other stuff that I have a hard time figuring out because I’m not a math PhD.

In Lisp I just write a macro.

Also, I agree strongly with the post below. I like Common Lisp precisely because it is old and unchanging. I like that I can get decades-old used books that are still current. I like that the next compiler version won’t break my code.

https://stevelosh.com/blog/2018/08/a-road-to-common-lisp/


On the strength of your first four paragraphs, I have to plug Julia. It doesn't have the benefit you point to in your fifth paragraph, of having a decades-old standard; stability of the core language is good so long as you aren't comparing it with C or CL.

But it has a full-fledged macro system, the kind where you can write an anaphoric if. It doesn't have the Zen of macros in a Lisp, but it has the power, and that's the important part. People have used it to add ML-style pattern matching and Pythonic f-strings, symbolic algebra systems which manipulate the source code, they're genuinely full-featured.

It also features multiple dispatch, every function is a multi-method and the type system is designed to support this. The community of practice is quite REPL-focused, although remote REPLs aren't as far along as they are in Lisp world. There are some cases where Revise can't invalidate old code, but most of the time it has the "hit save, use the new program" special sauce.

The core language is heavily inspired by Common Lisp, by way of Dylan, and it shows. There might be some other feature of CL which you miss, but it won't be macros.


These points scare me: https://gist.github.com/vindarel/15f4021baad4d22d334cb5ce2be...

I doubt Julia gives as many image-based tools for the REPL (no conditions and restarts?), no single-file binary? I guess Julia's ecosystem is less rich than CL's outside of science.


I realized I didn't address one of the main things you said, which was about image-based tools. That's better as its own reply I reckon.

Depends on what you mean by "image-based". Julia doesn't support reloading core dumps, but it has pre-compilation, and it has Revise, which will do everything it can to keep the REPL state up-to-date with changes to the source code. My experience is that it always updates state (silently) or fails and tells you, and when it fails can be predicted: if you modify the layout of a struct, or change an enum, it can't track that, so the prompt will turn yellow and you'll have to restart. You get the same yellow prompt (instead of green, and this is configurable to use textual cues btw) if there's a syntax error, but in that case, you fix the code and press enter and it goes green again.

Revise evals the minimum amount of code to update the program state, this is instantaneous for all practical purposes.

The Julia REPL is a massive improvement over any of the REPLs which ship with open-source Common Lisps, and head and shoulders above what comes with most other languages. It is not yet up to the standard of a commercial Lisp, or of the Emacs SLIME-mode approach, but the community lives in the REPL (and notebooks), so I expect to see the polishing process continue.


It's a young language still. Pre-compilation has brought "time to first plot" down considerably, and ameliorated many (but not all) of the specific complaints in that gist. One stands out, which is "lack of pattern matching", Julia is a language like CL where a language feature can be a library. MLStyle is arguably the best but there are a few[0].

Similarly, there's a start (heh) on a condition and restart system[1]. Julia macros are actually full-strength and that makes up for a lot. I would say that "less mature" is a fair comparison with CL but "less rich" is more of a judgement call. When people are in a mood to be critical of Common Lisp, the richness of the package/system ecosystem is a frequent target of that critique.

Binaries which aren't enormous is a main focus of development now, as I understand the timeline 1.11 will bring modest improvements (in the next few months) and 1.12 will be relentlessly focused on achieving this, it's a recognized problem.

I find Julia completely suitable for general-purpose programming, right now. Moreover I see it on the right trajectory. My hope is that when the trigger is pulled on 2.0, this will include a standardization process, resulting in an actually-stable language by about 2030. Note that Julia is closer to Rust-stable than Python-stable already, post-1.0 code which runs on the latest version (1.10) is more common than not, but I do think compatibility needs to be broken, once, eventually, to fix some bad decisions. It shouldn't have to happen twice.

[0]: https://juliahub.com/ui/Search?q=pattern+matching&type=packa...

[1]: https://news.ycombinator.com/item?id=39377229


Clojure is a different beast, not better or worse, just different.

CL doesn't have opinions, it's a toolbox.


I love programming in blah. It has made programming fun again.

I don't know if that makes me a blah person. I just know it makes programming fun for me.


This sort of conduct isn't welcome here.


What kind of conduct do you mean? can you explain? Or are you just mentioning random feelings like the comment I replied to with feelings of my own


It is boring to quote the guidelines, but you did ask:

> Be kind. Don't be snarky. Converse curiously; don't cross-examine. Edit out swipes.

> Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something.

and many others that at least suggest you could have expressed your sentiment in a more thoughtful way: https://news.ycombinator.com/newsguidelines.html


Popularity is a matter of luck. But suppose that the forces that drive luck somehow aligned themselves with promoting Lisp. There are ways Lisp would sabotage the luck being radiated upon it.

Programmers who get into CL will hit various silly obstacles:

- No standard way to express special characters in string literals.

- No standard Unicode support; no \u1234 notation in the standard. I/O with character encodings is implementation-specific.

- Weird pathname handling that is simultaneously too abstract, and too nonportable, which is oxymoronic. The pathname abstraction caters to features of operating systems that basically no longer exist. Yet at the same time, two CL implementations on the same modern OS (POSIX or Windows) cannot agree on all the details regarding how a pathname string (the real artifact seen by the OS) parses to a pathname object!

- The experience of just firing up a free CL implementation (no Emacs) out of the box and experimenting in its REPL is poor. CLISP has built in history recall and editing; I would recommend that.

- Ironically poor REPL experience in the free implementations, given that Lisp gave us that word. Only CLISP has built in editing and history recall out of the box. Telling newbies they have to learn a specific editor and its Lisp integration is poor and adds to the activation energy.

- Working with objects can be verbose with syntax like (slot-value point 'x) which is just point.x in many other languages. This can be shortened by defining accessors, but accessors abstract a lot. Given (x point) you no longer know that it's just a simple slot. It's a function call, which could be anything. Another thing is, would you give an accessor a one letter name like x, even if it's in a package?

- Newcomers to Lisp do have to learn the list processing. People coming from languages in which lists are collection bags get confused why their list x is not changing after (append x '(1 2)). If you don't use the loop macro and want to collect items into a list, you have to learn the ritual of push-ing items into a stack, and then using nreverse at the end. Guy Steele described a nice procedural list construction API in Common Lisp, The Language, second edition, but it's not in the standard.

- Working with multiple values is verbose, with forms like (multiple-value-bind (quotient reminder) (truncate 1 2) ...), and multiple-value-setq, and whatnot. Some pattern matching or binding libraries help with that.


> Weird pathname handling that is simultaneously too abstract, and too nonportable, which is oxymoronic. The pathname abstraction caters to features of operating systems that basically no longer exist.

This times 100. There are only a few places where the ANSI standard got it very wrong, but this is one of them. Logical pathnames were intended to abstract away different file naming conventions but what they actually did was cast in stone some anachronisms that are incompatible with modern Unix file naming (e.g. case-sensitive path names. Whoops! Can't handle that.)

The good news is that you can just ignore logical pathnames and use normal native pathnames, or use the illogical-pathnames package which largely fixes the problem.


- (i don't get the first point)

- unicode: major implementations support it.

- oh yeah. UIOP (shipped-in) and libraries fix pathname handling. https://github.com/Shinmera/pathname-utils - https://github.com/fosskers/filepaths - etc

- agreed, it's a pity for SBCL. See https://github.com/lisp-maintainers/cl-repl (although an editor with Slime-like integration is better)

- yeah, it's a choice to make during development. It could be point-x. Or using with-slots.

- yeah

- but multiple values are very helpful (reminder to the reader: they are not like returning a tuple. Return a list if you want). They allow to not break an existing ABI. You can add a return value and not change all the callers. Awesome.


Re: “”Ironically poor REPL experience in the free implementations, given that Lisp gave us that word.””

I just always wrap SBCL, etc. in rlwrap when started on the command line, in fact I alias SBCL to rlwrap <full path to SBCL>.

When I start SBCL in Slime, I use the full path in my .emacs file.

Easy problem to fix.


GNU/Unix command line tool mitigates Lisp problem, in other words.


I started learning Lisp recently. The text I was reading said people complain about the parentheses and I see a number of people stating that here. But that is not a problem when you have an editor that understands lisp and keeps track of all that for you. Just focus on the code and the editor will take care of the parentheses for you.


Agreed. At least you can see the parenthesis.

and seriously, what is the mental difference between say (print 'hello') and print('hello'), for me the first is easier. They both have the same number of parenthesis, but in the first I can see all the operations that go together.

Hating python every time I use it. Python is lies all the way down. "You don't need curly braces or anything to mark your code blocks, only indentation"... until you want a multi-line item, in which case you, well, wrap it in parenthesis.

foo = ("a very" "long" "string")

is valid python, and so is foo = ("a very", "long", "string")

but they give different results. vs (cat "a very" "long" "srtring")


> foo = ("a very" "long" "string")

> is valid python, and so is foo = ("a very", "long", "string")

> but they give different results.

That's because the former creates implicit string concatenation, while the latter is 3 distinct strings.

FWIW, while I love Python, I think implicit string concatenation is a huge anti-feature. It is a source of bugs and breaks the Zen of Python which states that explicit is better than implicit.

Your first line should by a syntax error, as far as I'm concerned.


I tried to learn lisp, and this may sound like a trite complaint, but I honestly just could not deal with all the parenthesis. Terrible ergonomics.


This is so often mentioned, it must be true. Or is it? To put numbers on it, I counted the occurrences of brackets, braces and parenthesis in Frank Busse's Arabesque cellular automata (just a small program for which I had the C version and a Common Lisp translation, which I happened to have looked at recently for no good reason). The C version had 86 of those characters, while the Common Lisp version 196. Yeah, more than twice as many, but can that truly be a chief reason to give up on a language?


It makes it very hard to, at a glance, parse what is happening.


I do recall difficulties with this in the beginning (the first few years in my case ;-} And I still find old LISP code (the one where symbols were still capitalized) hard to parse. With modern (say, since the 90s or so), well written style where the depth of the expression is indicated by the indentation, I trust that the editor indented correctly, which allows me to forget about the parentheses. I wouldn't attempt to find a syntax error in a CL program w/o an editor supporting the language.

Above might be interpreted as a case for a language with semantic whitespace and w/o excessive parentheses, e.g. like Python, but there I find it all to easy to introduce subtle bugs during refactoring.


Try an editor that supports Lisps.


This is the kind of answer which drives people away: you just told someone doing one hard thing to do another at the same time, and that also has near certainty that they’ll hit some task which is easy in their default editor but non-trivial in whatever new one you recommended.


I've written in Common LISP. Here's my port of the Boyer-Moore theorem prover to CLisp.[1] I've used original INTERLISP. I've used a Symbolics LISP machine. I once did a lot of work in Franz LISP.[2] And I'm partially responsible for AutoCAD going with AutoLISP as a scripting language. I've even taken a class from John McCarthy himself, at Stanford.

All that was decades ago. I haven't written a new program in LISP in decades. (In later years Python, Go, Rust, C++, and when forced, JavaScript.)

* Early thinking was that programs needed to be able to modify themselves. This turned out to be a niche feature. It was a holdover from the early days of programming, where storing into the code was considered a normal activity. Von Neumann was into that. To index into an array, load instructions were modified in place. Once index registers were invented, that mostly became unnecessary. Languages can be too dynamic. You pay a price for that. Python suffers from the design feature that anything can store into anything at any time. This blocks most compile-time optimizations.

* Lists as a primitive are OK, but modifying lists all the time isn't what you really want. Most languages have settled on a useful set of collections today, and their implementations, especially hashes, are highly optimized. Python pretty much got this right, and other languages now use roughly Python's various collection types.

* Saving the entire state of the system, instead of having source files, was a really bad idea for anything that had more than one person working on it.

* The overly clever macro system makes code hard to maintain. (I tried to port [2] to Common LISP. Got about half way. It has the original Oppen-Nelson simplifier inside, the first of what's now called a SAT solver. Originally written in MACLISP (MIT's Project MAC, not Apple Macintosh), and ported to Franz LISP by Oppen and Nelson, it has too many clever macros that I wasn't able to completely figure out a decade ago.) This is a generic problem with macro systems, of course, which is why C deliberately had a weak macro system.

LISP is a blast from the path. It's fun for retro reasons, but things have moved on.

[1] https://github.com/John-Nagle/nqthm

[2] https://github.com/John-Nagle/pasv/tree/master/src/CPC4


Thanks John, you make great points on the advantages of ‘moving on’ from Common Lisp. I still use Common Lisp a fair amount just because it makes me happy to do so. While I need to use Python, using Lisp languages is just a preference. Because I am retired now, I have thought of starting with something bare-bones like Chez Scheme and making a long term hobby of building up my own environment mostly just for myself. The author of Gerbil Scheme started with the excellent Gambit/C Scheme and did this, and now the Gerbil ecosystem is really cool.


yet no other language gives so many tools to the developer… quantum companies would disagree. https://github.com/azzamsa/awesome-lisp-companies/

(BTW: CL isn't Smalltalk which isn't uniquely that anymore, we do use source files and we can compile single-file binaries. My web app weights 35MB, starts up in 0.4s (or 0.01s without core compression))


Everyone, if you don't have a clue on how's Common Lisp going these days, I suggest:

https://lisp-journey.gitlab.io/blog/these-years-in-common-li... (https://www.reddit.com/r/lisp/comments/107oejk/these_years_i...)

A curated list of libraries: https://github.com/CodyReichert/awesome-cl

Some companies, the ones we hear about: https://github.com/azzamsa/awesome-lisp-companies/

and oh, some more editors besides Emacs or Vim: https://lispcookbook.github.io/cl-cookbook/editor-support.ht... (Atom/Pulsar support is good, VSCode support less so, Jetbrains one getting good, Lem is a modern Emacsy built in CL, Jupyter notebooks, cl-repl for a terminal REPL, etc)


Back in the day, it required machines that were more powerful than what C programs could run on. By the time machines caught up and Common Lisp was standardized it was too late.


Lisp was sort of a testbed for extravagantly expensive features, many of which (not all) became table stakes for newer languages as they got better at writing compilers for much bigger, faster computers.


Somebody famous said something like "Lisp makes hard things easy, and easy things hard", which finally, after all these years, clarified it for me.

(It was quoted on HN in a previous discussion, and I was impressed with the credentials of the person quoted, but I don't have the reference handy.)


I've written a great deal of Common Lisp and the only "easy things hard" I've encountered are fast matrix multiplications -- because there are many ways to do this and they each have different tradeoffs, and implementing symmetric crypto algorithms that assume 32-bit word sizes. Because Lisp integers automatically grow when necessary, forcing arithmetic to stay within a 32-but limit is hard do do in pure Common Lisp efficiently. The solution is to write bottleneck routines in assembly, which the Lisp compiler I use (CCL) facilitates.


I haven't written Lisp professionally, but my impression is that one place Lisp comes short is forcing convention:

Every program, every module, is potentially its own DSL, so good luck getting programmers to agree on a convention.

At least with more rigid languages like Java or Rust, when you open a file, you'll agree what language it is.

Haskell has this problem, too. You need things like "Boring Haskell Manifesto" to discourage too much magic.

Haskell has "pragmas" that modify the language extensions that turn Haskell into a space language. Pragmas can be enabled on a per-file basis.

I like how the Rust toolchain addresses this by letting you gather things like "stable/nightly", "feature flags", "linter suppressions" in a config file at the project root, so that a team can agree on a set of conventions and have the toolchain sync with those decisions made in one place. This isn't strictly speaking programming language, but tooling, but it plays a big role in team governance and cooperation.


> Every program, every module, is potentially its own DSL

it isn't, don't worry. Problem solved ;)


I'm not using Lisp in production, so no problem to begin with. :-D


You want to learn programming. Lets get started - how long does that take to bang out 10 exercises? Order the estimated results from quickest to slowest. Here's my estimates (don't like them, show yours! It'll be interesting):

  - Scratch
  - python/perl/ruby/javascript
  - C/Java
  - Swift/Rust
  - Anything else running on the jvm
  - 
  - a whole lot of daylight
  -
  - common lisp.
And many if not most will fail to get to the first exercise, those that do will start it well after all the rest have completed 10.

Land of lisp seemed to be the best learn lisp book and it couldn't even manage to limit itself to one implementation of common lisp that you could use for the entire book.

The situation is ridiculous.


I think had I started with a lisp dialect like Racket and the book The Little Schemer, the speed of getting through those first exercises may have been just as fast or faster than the Ruby and JavaScript I started with.

Lisps are hard to switch to but I disagree that they're hard to start with.


Scheme is not common lisp.

But you know where you place it on the list. I did sicp w/ racket, a 10 y.o. I know tried simply scheme w/ racket. I'd put it next to anything on the jvm (including clojure) myself. The 10 y.o. puts it way behind python & swift.


I am curious about the reason you put Swift so low in the list. Like to me it's as easy to output a program in Swift as it is to make one in JavaScript. (except if you count compile times, last time I used it I already thought they were way too slow)


mac only. iirc you have to install xcode too. Compare to perl & python already installed on a mac and relatively easy to set up on windows.

But I'd still call it very high on the list relative to common lisp, which is the point.


Interestingly, Scratch is based on Logo, which is based on Lisp, although that Lisp is older than Common Lisp, so that means a Lisp is at the top of your list.

If you want a really nice language derived from Lisp, try REBOL or Red (derived from REBOL).


You’re biasing yourself to what you already (probably looks like C). There is nothing more difficult about some paren except you didn’t learn it first.


Nup. Not the language.

See how long it takes you to set up a Common Lisp implementation from scratch. Eg steel bank. Better yet, watch a kid try it on for size. Land of Lisp isn’t all bad but they can’t limit themselves to just one!

Brackets are beside the point.


Why would a professional judge the quality of their tools by the experience of children?

If a 3 year old has trouble using a hammer, you wouldn't conclude that hammers are difficult to use.


This is a discussion of why common lisp is not particularly popular. I observe most professionals can't program in it (it's not popular). Then look at why. The very first hurdle: epic fail. Pretend it doesn't matter?

On the other hand if you don't care that's perfectly reasonable and there's not much to talk about.


apt install sbcl

sbcl —script hello.lisp


Try it. Point someone at trying to learn programming at sbcl. What percentage do you think have any clue what you just wrote let alone run a debian based distro or even know what that is?

Compare that experience to choosing python. Forget the actual language just all the overhead. It's not a close run thing no matter how angry common lispers get about it.

This is my exact experience of common lisp. People aggressively pretend there's no problem other than other people are stupid and have broken minds because they didn't get functional early enough and so on.

Land of Lisp at least tried, right? And did a vastly better job than anything else I've seen. Couldn't manage to do it all on sbcl though, huh? That's a shame, isn't it? Why do you think the best effort failed in something like that?

https://nostarch.com/lisp.htm


I don't understand, is installing and running python easier than running an install command?


Oh come on. You mean installing with apt on debian based linux? Ok so compared to not having to do that. Both easy once you're proficient at debian, one easier.

Now do mac.

Now do windows.

Installation is only step /one/ in smashing out your first ten exercises.

Pretending something is easy because you want it to be does not make it so for others.


OTOH, I remember reading somewhere that Tanenbaum was happy when Linux came out so that people would stop asking him to make Minix into a real "usable" operating system. There may be something to be said for letting Lisp remain more conceptual/ideal than practical and allowing other functional languages take on the "warts" associated with being used by everybody.


This is still the best explanation IMO:

https://www.dreamsongs.com/RiseOfWorseIsBetter.html


A plausible reason: because no big company is backing it with money.

This industry will mostly use whatever Google/Microsoft/Meta sponsors.


Google sponsors Lisp compiler development.


Personally I'm not especially interested in programming in a language with a separate function namespace. It makes much more sense to me to treat functions more like any other value in the language.


Generally speaking, the best things are not the most popular. There are probably a few exceptions such as classical music, but I've found this rule of thumb to hold in many different domains.


People’s ability to process complexity must play into where the peak is.

I large culture consisting exclusively of genius composers would no doubt develop styles of music that would relegate our pinnacles of classical music to their “pop”.

No doubt there is a bell curve of how easily each of us intuit novel abstraction, vs. benefit from more predictability (i.e. restrictions, enforced patterns).

Mainstream languages are going to be more predictable and restrictive or patterned, than programming languages auteurs might prefer.


Paraphrasing the point at the end, there is no company that has the financial incentives to promote it. The companies that do have financial interests in CL are ironically not incentivized to promote the free ecosystem around it. For the same reason that commercial C compilers are not really a thing anymore.

This is, of course, a simplified view of things. For one, I think Intel still pushes a compiler some. That said, I think it is valid enough, in that Intel isn't pushing the compiler for the sake of the language it compiles, but to push some optimizations that they excel at.

There is a question of how explanatory this is. I think it is fairly accurate to say that the marketing and general outreach for many of the successful languages today helped build on their popularity to the point that we know them today. It does not explain the seed of any success, though.


Afaik intel compilers are based on llvm nowadays


Right, I don't know the core details right off, I just meant that it was easy to think of some commercial C vendors that still exist. I think it is fair that there is a difference in having a corporate sponsor, and having a organization chartered to sponsor. (I also have to ack that I don't know of many other C vendors, nowadays?)


Why is Ferrari not the most common automotive manufacturer? Clearly it has something to do with all the pistons.


If Ferrari and Toyota cost the same amount of money, you'd see more Ferrari. SBCL and Python are both free.


Ecosystem.

For example, the Common Lisp ecosystem never converges to a single implementation of any single "thing".

Take Rust. You may not like Serde for serialization, but the ecosystem has converged to it. Similarly, Tokio. You use them until you understand why you need something else and then bite the bullet of going against the tide when you can't make them work anymore.

Okay, so what serialization has Common Lisp converged to? Uh, yeah, about that ...

"Lather, rinse, repeat" this for any value of <X> (serialization, async, objects, concurrency, etc.) and anyone sane quickly reaches the conclusion "Just say no to Common Lisp".


That’s also true of c++ and C and doesn’t hold them back.


But it is easy to roll your own for your own problem area!

—-> Everyone rolls their own!

Its an interesting problem.


Languages that insist on prefix function syntax (`(f 'a)` instead of `('a f)` or `'a.f()`, etc) are always going to be less popular because it reduces the number useful possible suggestions an IDE or REPL can make to a programmer typing a program. You have to know what functions you want to call and the IDE can "fill in the blank" with arguments that are in scope. But you need magic to get the IDE to tell you what functions you can call on an argument.

Julia suffers from this too, fwiw. Multimethods in general can be strange but useful beasts, but I don't know of any IDE that handles them well. And sure C does this too. But C isn't exactly the model of useful language that lets a programmer dynamically explore what programs they can compose, is it?

Normally, I think most syntactic arguments against languages are bad. But having written in fancy PLs that don't have any kind of postfix/method syntax and those that do, as well as writing language servers and compilers, this is the one syntactic thing I feel very, very strongly about.

LISP will always suck for beginners because in Python and C++ and Rust and Swift (etc) they can add "." to the end of a value and the IDE will remind them what methods they can call, or explore what's available. The IDE can even weight which things to suggest. Other people talk about package managers and ecosystem and so on, but this one syntactic choice is something overlooked by many people when talking about how languages grow userbases. Don't overlook it.


I agree with the conference and foundation part. I've been to many lisp conferences, but they are still extremely weird, compared to other languages conferences.

I'm pretty sure it's the people.

Lisp is a lone man sports. There's not much community spirit. There's not much github, patches are not accepted.

In general. Of course there are many great people around, of the anti-academic background. Who tried to build communities. But the ecosystem lacks a lot still.


> I'm pretty sure it's the people.

Glass house?


> I'm pretty sure it's the people.

> Glass house?

A sympathetic reading of that might be: “I’m pretty sure it’s the a-collaborative go-it-alone culture of many of the practitioners”.

Lisp’s famous strength at enabling coding styles tailored to every specific domain, may also be its weakness with regard to low friction code sharing & adoption.

That, and the distance between Lisp’s high dynamic flexibility vs. more restricted imperative or functional coding styles that are easier to optimize at the low level, may encourage more bespoke development practices.


Is that something you had heard or guessed? Did you attend a Lisp conference and talked to the people, where they were coming from (companies, universities, research labs, ...) and in what domain they were working on?

Let's say, ACL2, a theorem prover written in Common Lisp. Unless what Mr. Urban tells you about Lisp users, they have a github repository. They have user/developer meetings. They even presented their stuff at Lisp conferences (I have attended one myself, where I saw a talk about it), wrote books about it, published hundreds of research reports, ... The user group shares > one million lines of Lisp code ( https://github.com/acl2/acl2/tree/master/books ), with many more lines of code developed by users.

https://github.com/acl2/acl2/

Workshops:

https://www.cs.utexas.edu/users/moore/acl2/workshops.html

How does that fit into your theory and what Mr. Urban wants us to believe? Here we have a shared large Lisp code base used at companies like Intel, AMD, ARM, ... where people collaborate over the Internet... how does that fit into your "go-it-alone culture of many of the practitioners" theory?


Another aspect that perhaps has changed in the years since - when I was playing with Common List many moons ago, the community in comp.lang.lisp was the place to go, but also rather easy to bounce off. I mean, amusing flames, but yeah, asking a genuine question, the fucking manual read beforehand to ensure not already answered in Hyperspec, often led to rather unpleasant replies. Erik Naggum was particularly abrasive.


I remember Erik Naggum. He was always worth reading, but yeah—incredibly abrasive.


He was so often frustratingly right, too.


I particularly remember an extended rant that Lisp == Common Lisp, and Scheme is not Common Lisp, and therefore Scheme is not Lisp. I’m oversimplifying—Erik gave specific reasons—but that’s what it amounted to. Derailed an entire conversation, IIRC.


I think I remember that one, it was something about a Lisp being a Lisp-2 by definition. I shook my head in disagreement at the time, and still do.


Heh. Yeah, that was part of it.


That's one thing where he was right.


> many moons

that must be more than 15 years ago...


I haven’t used Lisp in a long time. But I used to, and also implemented my own mini Lisp more than once in a panic to get some functionality working in a quick and dirty way.

Something I have not seen here is the advantages of type (dynamic or static) for channeling consistent library symbol use.

For instance, one of the undersung benefits of class style object oriented code, or any type dispatch, is how it helps enforce the right set of functions to use on some data (i.e. the methods of that object).

This cohesiveness between a bucket of symbols related to a data type makes coding and sharing easier. In whatever form this takes, across languages.

Lisp’s flexibility doesn’t provide this kind of namespace modularizationthat I recall?

Am I out of date or forgetting something?

This kind of helpful context sensitive symbol organization is a big factor for simplifying code sharing at the “syntax” level. Reduces mistakes, narrows choices (in a productive way), etc.

Without it, adopting a lot of outside code is much harder.


> what we really need is one person who will lead a main CL only conference

Well, if you're volunteering...

All joking aside, I'm not particularly bothered with the relative lack of popularity CL has, its force-multiplying powers make it easy for a relatively small community to create and maintain a decently sized ecosystem.


I tried before because I read a lot of praise about it. But it seemed to me that there's not much of a standard library or included batteries (at the time, I was looking for something that can open image files or compute FFTs). Perhaps the problems I wanted to solve just weren't a good fit.


Lissssp – lotss and lotss of ssilly parethesises! We hates it!

– Quoted by memory from comp.lang.lisp years ago. (Sorry, can’t remember by who.)

Really, I get the parenthesis hating: It is truly terrible to have to deal with them unless you have good editor support and have learned to use it. But then a funny thing happens: The parentheses essentially vanish! You just don’t read parentheses anymore, you read the indentation instead. In my own emacs setup, I have in fact chosen to de-emphasize the parentheses to the point where they are barely visible. It makes the code so much more readable.

Good editor support means at least semi-automatic indentation of code. Unindented code is of course unreadable, and wrongly indented code is worse. But that is true of any programming language, isn’t it?


Because despite the enthusiasm of the advocates, it's not actually the most useful programming language.

"But I used it for X and it was insanely productive!" Great, but there's a lot of programming that is not X programming.

"It's just that the rest of you are not enlightened!" Right... the maharishi's people tell me the same thing.

"It perfectly fits the way your mind works!" It perfectly fits the way your mind works. It doesn't perfectly fit the way most of our minds work.

Lisp winds up not being the most productive language for the vast majority of programmers. So it's not very widely used.


“It’s the macros, Stupid!”

Seriously. Macros have to be understood a priori to be readable. Macros make the cognitive load of the language very heavy.

Contrast that to Ruby which can do rich DSL without macros. It’s much easier to read and figure out even when it gets complicated.

Lisp is a beautiful language. But it hits a wall. Macros were made to hop that wall, but ultimately hurt the language.

I think this can be fixed with pattern matching and perhaps types, but it will take a very smart programmer to pull it off and it will take Lisp in a direction that old die hards are probably uncomfortable with.


For me it's the parentheses. I'm sorry, I know it sounds childish, but it's true.

Just look at this picture:

https://www.semanticscholar.org/paper/The-programming-langua...

We call that "line noise".


That's code from 1966. It resembles modern Common Lisp the same way "Beowulf" resembles "The Three-Body Problem."


A good deal of my work involves moving data in and out of databases. For the three Ps--Perl, PHP, Python--it is trivial to set up the libraries for the commonly used databases, after one can move on to work. Is that the case for Common Lisp?

Another part of my work involves the web. There I can use mod_perl, mod_php, and mod_wsgi. What about Lisp?


(I once) (worked at a company) (that made (Lisp) machines) and (sold them internally (until the factory managers (wised up) and said (the programmers (are great) (at creating (a functional prototype) but (invariably left for higher pay elsewhere (before completing the job) and (try were always worse off))))


It’s more of a toolbox to build languages than a language.

So every commonlisp codebase has a different flavor, and anachronistic semantics.

No matter how good you are, this is difficult to deal with.

Sure, for config files or writing extensions for your favorite piece of software, you’d make the effort. But most software development is not in that category.


Is it actually that good of a toolbox for building languages? I don't think people really use it much for that either


It's not like you'd often create a named language, it's more like accidentally defining a DSL along with libraries when working on something in a lisp with a decent macro system.

As a result, all the client code looks like it's written in a bespoke language. This, while it's a lot of fun, creates write only code.


It's the learnability of the language. Remember BASIC? You could show someone a FOR ... NEXT loop and they would completely understand. After a couple more 10 line programs and you can safely hand over the keyboard and handle the "how do I?" questions

You had to show very little before they understood. Now try Lisp ...

M: This is a list

S: What can I do with it?

M: By itself nothing but trust me it is important. This is how we take the head of a list

S: Cool, why would I want to do that?

M: We'll get to that later. This is how you take the tail of a list

S: ...

M: This is how to append to a list

S: Is this going anywhere?

The number of things you are shown but do not understand keeps piling up before they are supposedly going to magically work together to do something underwhelming (when BASIC did very little it took very little to do it). Add to that the function names were either cryptic, "cddar" anyone - "EQ" and "EQUAL", or insanely long

You have too much to learn before you can make sense of anything. Least that was how Lisp was taught to me in the 80s :)

I self taught Forth and Prolog because with both once you learn something you can do something


I don't know why you're being downvoted; I completely agree with this. It's hard to put your head into the mindset of a beginner after it's been in the deep end of the pool for so long. Stuff needs to make sense now and in isolation rather than an ever-growing stack of items that will be explained later. It's a matter of putting stakes in the ground and building incrementally. One could argue that lists and heads and tails do make sense on their own and isolation, but they're maybe not the right primitive, because all the things that beginners might want to use "intuitively" have to be first built out of lists. Lisp is a reductionist language, and that's not good for beginners.

Readable code needs to be not subtle, low on magic, and self-explanatory. The last one is hard to quantify and make objective, but is imperative in making an approachable language.


    (set x (list 1 2 3 4 5))
    (for item in x 
      (do stuff to item))
Now try to explain BASIC, but do it by first explaining how to create and manipulate a block of memory.

You are conflating language differences with the real argument which is the decades-old debate about whether introductory courses should be practical or rigorous.


But that is my point. When trying to teach Lisp they would teach you all about the 100 ways to chop up lists without showing you why you would even want to. Your example would not have appeared until the 4th or 5th lecture and would have required the definition of a recursive function (two lectures on recursion alone) to achieve the same thing

And as for BASIC why would you need to "explaining how to create and manipulate a block of memory". This is not something that the BASIC programmer needs to know to write a program

This is just the sort of elitist crap that gatekeeps becoming a programmer. You can drive a car with only the most superficial understanding of how a car works and it does not affect your ability to get from A to B effectively

The answer to this decades old debate is simple, after each lecture the student should be able to do more than they did before they entered the room. At the end of the course they should be capable of teaching themselves from the available resources


The vast majority of people who want to create something that executes on a computer are not inclined to write in a language that more closely resembles parse trees than it does human language. To many, Lisp is nearly as inscrutable as an intentionally esoteric language.


It is kind of tricky to read and probably a bit part of Python's success is down to it being maybe the easiest to read, especially for people who are not very techy as it reads kind of like English.


Because languages are created with the purpose in mind and not a single one can tick every box. Also there are factors like user / org preferences, policies etc. etc.

There is absolutely no need for one size fits all solution because it will be always wrong.

Looks like largely rhetoric question


This argument seems flawed to me because of the existence of LispWorks. LispWorks makes money from the growth of Common Lisp and people buying their interpreter. It would be in their best interest to grow the language.


A lot have already been said about it:

+Lisp Lost the train of microcomputers. They were "toy machines" that needed low level programming like assembly or forth or later C to do anything remotely similar to what Mainframes could do.

+Common Lisp is a mess, a compromise, like Deutsch (a language created to take something out of every variant dialect), designed by committee language. Better (opinionated, created by a person) alternative Lisps exist, like Clojure.

+Lack of (affordable, easy to use) Visual tools to develop, like Visual Studio. No. Emacs is not easy to use for newbies. Visual structured editing for parenthesis have existed for decades now but those tools were not available for normal PC and even today are very expensive, as the market for them are the companies that bought the expensive Lisp machines in low volume.

+Lack of access to the C libraries. Do you want to use anything modern like "dear imgui", iOS libraries or whatever? Bad luck.

+Competition catch up. Other languages copied(badly) the Lisp ideas, lambdas and real macros, functional programming. IMHO they did it wrong, but they are good enough for most people, while not having the problems of CL, like being able to use the C libraries as python and Lua can.

+Other Lisp are working fine. Clojure for example can use the java libraries or even some versions the C Libraries.


> Lack of access to the C libraries.

???

I recently started learning Common Lisp for fun (and fun it is!) and the ease of accessing C libraries was one of the things that surprised me in a positive way.

Using https://github.com/rpav/cl-autowrap one can simply write (c-include "file.h") and the API defined in "file.h" is accessible from Lisp. I can't think of a simpler way.

Even without cl-autowrap, FFI using https://cffi.common-lisp.dev/ seems simple enough.


I think he was talking about the late 80s and the 90s.


> Lack of access to the C libraries.

Isn't CFFI enough for that?


Essentially all the Lisp features except for s-expression syntax and attendant macros have been adopted by popular mainstream languages. CL has already given everything it had to give.


The Common Lisp condition system is far superior to any alternative in any other language.


Not sure about that, but it's true it hasn't been adopted. There's also some CLOS stuff you can't find in modern languages.


wait a few years and some web language will "rediscover" it :p


not true, sorry! And if some languages took features, the combination of them is unique in CL. Image-based REPL interactivity, condition system, interactive debugger, restarts, compile-time computing, compile-time checks, incremental typing, speed, a different object system, stability, single-file binaries, connecting to a remote system…

Here we restart a buggy program from a single point in the stack: https://www.youtube.com/watch?v=jBBS4FeY7XM so we don't re-run everything from zero.


Those cases are well served by Clojure, which gives you the macros + a modern, professional, battle-tested standard library (via Java).


Every successful language have become popular because of a succesful platform or killer app, not on the merits of the language itself.


If Racket has what they're looking for and CL doesn't, then that's proof that the assertion is wrong, because Racket is far less popular than CL.

The correct answer to the question is the obvious one: the parentheses. Visually, semantically, practically, expressibly, and legibly. And the typelessness, and the ecosystem, and in general the idiosyncrasies, almost none of which add value.

All the standard counterarguments have been done to death, none of them are any good, but more importantly, they're not how popularity works.


The same reason as why Esperanto is not the most popular spoken language. It's historical reasons and network effects.


No, it's difficult to read and understand. It's a parenthesis circus. For example -

https://github.com/dimitri/pgloader/blob/master/src/pgsql/pg...


I clicked thinking I was going to see some gnarly Lisp code. It's out there!

This, on the other hand, is perfectly legible if you know the language. I'd say it's even perfectly legible if you don't, but how would I know?

There are a couple good examples in there of what I said in another comment, about how, quite aside from getting the hang of reading and writing sexprs, one also has to learn a rather more foreign discipline of building up linked lists in the idiomatic way. Which is a greater barrier than learning to read `(and foo bar)` as `foo && bar`. That part really doesn't take long.


Esperanto is difficult to read and understand. It's an accent circus.

I'm having fun with Typescript these days, but parentheses are what I miss most from my Scheme days. They're objectively better.


“Read” the indentation - ignore most of the parentheses.


Here are so many responses, I begin to wonder whether CL is actually used less today than it was thirty years ago.


* You can't google snippets on stackoverflow

* Packages are harder for divergent languages

* Momentum

Hard to come up with more words to explain it


it's a kitchen sink full of old baggage like car, cdr, caaadr, etc.

clojure/cjs/babashka, fennel, janet, racket, etc make more sense to learn.


Lisp seems to be a language for individualists, which isn't a great way to build a community and beyond.


due to a shortage of parentheses in cambridge?

apologies to SKB.


C does what I say but not always what I mean.

C++ is too complicated to even bother, and looks a completely different language every five years; I have yet to meet anyone that knows its syntax fully (C++ compilers included). Writing C/C++ means spending your time managing memory and then debugging memory management errors rather than thinking about the problem domain.

Python is very slow and has horrible package management.

Rust compiles slowly and the borrow checker takes a long time getting used to.

Java is bloated and Oracle-proprietary.

No version of Julia could even compile the example of the Julia book I bought, nor some of its own tutoials.

Perl is write-only.

...and CommonLISP? I know important and successful systems have used it in the past, but haven't heard anyone using it for anything I needed recently (but that could be due to my area, search engines, natural language processing and machine learning, where Python libraries - written in C++ to be fast enough - dominate the research frontier).

LISPs incl. CommonLISP and esp. Scheme are elegant, minimalist (Scheme in particular) and lack powerful IDEs and library support (at least since LISP machines disappeared), and excess parentheses make for a write-only experience, especially if you have to read the code of others (your own code may make good use of functional abstraction).


> No version of Julia could even compile the example of the Julia book I bought, nor some of its own tutoials.

This was either many years ago, or is a skill issue. Post-1.0 Julia backwards compatibility isn't perfect, but it's held to a high standard. Being unable to run its own tutorials is not a thing that actually happens, and the language isn't responsible for bugs in whatever book you were referring to.

If this was pre-1.0, sure, that's what a version starting with 0 means. But after? Skill issue.


Popularity has nothing to do with the tech. WordPress is popular and it's shit. Ms word is popular and there are free substitutes. Success is about a form of politics and coercion with mvp. Emphasis on the "minimal."


((I’m(honestly (not (sure,) it’s hard) to believe)))


(i)(don't)(know)


(((its (the (syntax (especially (due to lacking function overloading and maps)))))))))))))


What's wrong with CLOS generics? Not only does it have function "overloading", it has it on any of the parameters while still being object oriented.

    (defclass Shape () ())
    (defclass Rectangle (Shape) ())
    (defclass Ellipse (Shape) ())
    (defclass Triangle (Shape) ())

    ; Having a defgeneric is not strictly necessary in CLOS. The code would
    ; work without this definition. However, this is good practice as it gives
    ; us a natural place to document the generic interface methods are expected
    ; to implement. It also lets us add a default handler with a meaningful
    ; error message, if no suitable method was found in some case.
    (defgeneric intersect (x y)
      (:documentation "Shape intersection")
      (:method (x y)
        (error "Cannot interesect these shapes")))

    (defmethod intersect ((r Rectangle) (e Ellipse))
      (format t "Rectangle x Ellipse [names r=~a, e=~a]~&"
              (type-of r) (type-of e)))

    (defmethod intersect ((r1 Rectangle) (r2 Rectangle))
      (format t "Rectangle x Rectangle [names r1=~a, r2=~a]~&"
              (type-of r1) (type-of r2)))

    (defmethod intersect ((r Rectangle) (s Shape))
      (format t "Rectangle x Shape [names r=~a, s=~a]~&"
              (type-of r) (type-of s)))
>maps

make-hash-table


It has both of those features in several forms.


The lack of compatibility in python really is a disaster...

It's so great and new that it's not compatible with last week's code...




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

Search: