back
91 comments
"From the perspective of 2024 though, I think that the most appropriate reaction is to marvel at the ambition of the UC Berkeley team, commercially successful or not, and to be equally impressed by how relevant (with the possible exception of LISP) the ideas in SPUR would become decades later."

I thought it was pretty widely accepted in the programming language community that Lisp has had a massive influence on the development of programming languages in general. I know it's not the only game in town, as it were, and that there's been lots of other interesting developments, but still. To imply that it hasn't been "relevant" seems like an uninformed comment to me.

The influence of Lisp is widely known among PL researchers and practitioners (such as compiler writers), but there are many people in computing who are unaware of PL research and who have little exposure to Lisp except for Emacs users and maybe a few exercises in Scheme during college. Thus, the contributions of Lisp are constantly rediscovered by those who just come across it. Over the decades mainstream programming languages have incorporated features from Lisp, and there are still Lisp features that haven’t made its way into mainstream programming languages.

Lisp is by no means the final word on programming languages, but its flexibility from its S-expression syntax to macros to its metaobject protocol makes it easier to bend the language to fit the problem rather than the usual approach of making the problem fit the implementation language, and this flexibility remains an enduring trait that continues to attract people.

Dynamic typing and garbage collection didn't really catch on
Gosh. Emblematic event: the journal Lisp and Functional Programming changed its name to drop Lisp, around early 90s iirc. So much of Lisp is super mainstream since before some programmers were born, now, that those aspects are no longer linked with it. And dynamic typing and GC are fucking ubiquitous compared to their status in the 80s.
I was annoyed at first but I think JonChesterfield was joking
It's an obvious joke from the perspective of javascript or literally true for rust enthusiasts. It's fascinating that various different branches of the language tree agree that lisp did some things right and completely disagree about which things those were.
I guess nobody is using Python or Java.
Yeah, and REPLs. Totally not a thing :-)

Remember when it was highly controversial for Java (and then C++) to get lambda expressions and many treated us as egg-headed academic nerds for wanting those things?

I sure do.

In java lamdas are just a syntactic sugar for anonymous inner classes, with the same resulting limitations, so one can either argue that java always had lamdas, but with weird verbose syntax, or that java does not really have lambdas (in which case C++ and Python also do not have lambdas, as the semantics are similar).
I miss those days of deep granular CS projects that strove to create the most efficient minimalist systems possible - seems the opposite of today's prolific jungles of libraries and linkers. Then again I consider Jonathan Blow a prophet in the desert of the real...
The emperor has no clothes.

Numpy 2.0 came out two days ago and it's chaos in the whole AI ecosystem. I'm not sure how much money we're wasting on that but I wouldn't be surprised if it's on the order of a billion dollars - suppose there are 50,000 people getting paid on the order of $1,000 per day each spending the two week dealing with fires over the next year: $500,000,000

Did something major break on 2.0?
The made breaking changes in the ABI... not like there weren't warnings. "Breaking changes to the NumPy ABI. As a result, binaries of packages that use the NumPy C API and were built against a NumPy 1.xx release will not work with NumPy 2.0. On import, such packages will see an ImportError with a message about binary incompatibility.

It is possible to build binaries against NumPy 2.0 that will work at runtime with both NumPy 2.0 and 1.x. See NumPy 2.0-specific advice for more details."

https://numpy.org/devdocs/dev/depending_on_numpy.html#numpy-...

> The made breaking changes in the ABI...

They bumped the major number. That's fair play. There has always been a lot of slouching wrt versions in python. That's not numpy's fault. Too bad they're getting the black eye for it. They could have avoided it by making a new dependency name ("numpy2"), but that sets a shameful precedent, so I give them credit for not copping out.

It broke our builds. Because Numpy doesn't use semver there's no way to specify compatible versions constraints. One of our dependencies had a `numpy>=1.something` constraint but wasn't actually compatible with Numpy 2.

We need to use a lock file really, but `pip` doesn't support that - you need to use a better package installed like `uv` to get this standard feature.

Is there any modern CHERI-type approach to this? I don't know if the idea is a dead end or not, but I'd be very interested to see a modern processor that is made with something more symbolic and lispy than current x86/aarch64 designs which still feel to me like they're made for C.
Back in the 80's Intel tried to make a CPU designed for high level languages.

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

It was a commercial failure.

The iAPX 432 programming model is a stack machine with no visible general-purpose registers. It supports object-oriented programming, garbage collection and multitasking as well as more conventional memory management directly in hardware and microcode. Direct support for various data structures is also intended to allow modern operating systems to be implemented using far less program code than for ordinary processors.

SPARC felt like it was made for C. x86 feels it was made for punishing developers.
Actually SPARC have four instructions that are directly meant for efficient implementation of Lisp/Smalltalk that came from SOAR and SPUR. Using that on top of some kind of Unix is shall we say problematic (in same way that x86 BOUNDS is mostly useless), together with few other “fast conditional trap” instructions in SPARC ISA, but it is there.
> Actually SPARC have four instructions that are directly meant for efficient implementation of Lisp/Smalltalk that came from SOAR and SPUR.

You are talking about the tagged add and subtract instructions, TADDcc/TSUBcc, and their trapping versions TADDccTV/TSUBccTV.

> Using that on top of some kind of Unix is shall we say problematic (in same way that x86 BOUNDS is mostly useless), together with few other “fast conditional trap” instructions in SPARC ISA, but it is there.

I've never tried using it, but why is it "problematic" on Unix? From what I understand, both on Solaris SPARC and Linux SPARC, the kernel translates the tag-overflow exception into a SIGEMT signal with si_code=EMT_TAGOVF, so you can catch the tag-overflow exception by installing a SIGEMT handler. On Linux SPARC, I think SIGEMT is only used for tag-overflow, whereas on Solaris it also is triggered by CPU performance counter overflow (EMT_CPCOVF)

I think TADDccTV/TSUBccTV are problematic in the sense that they are officially deprecated, and only supported for 32-bit overflow, not 64-bit overflow. The docs say to use BPVS instead (so branch on overflow flag instead of trapping an overflow exception)

All that said, this all has very fading relevance now, given how moribund SPARC is. Oracle has no plans to introduce any further SPARC CPUs, the SPARC CPUs they currently sell were released 7 years ago, and I expect they'll stop selling them sooner or later. Fujitsu has announced they'll end SPARC server sales in 2029, which is only 5 years away now, and although they were at one point talking about one last CPU after the current M12 generation, I doubt that's still happening.

The inefficiency is about going through kernel that will then dispatch some signal and the signal handler has to analyze what exactly happened, that is not a slow path, but ridiculously slow path.

OTOH, my view is somewhat LISP-centric and just implementing + by passing the arguments to taddcctv would be problematic, in the Smalltalk world, implementing SmallInteger>>#+ like that makes sense.

and c++ was made for punishing silicon a perfect match
Software engineers had their revenge. For now.
I fell through the rabbit hole on this one, and found this post with a delightful video from the The Computer Chronicles about RISC, circa 1986:

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

I'm so glad we have these sorts of things archived!

FWIW, that[0] links via abstracts to tech report pdfs[1][2][3].

Hmm, looks like https://www.softwarepreservation.org/projects hasn't been submitted to HN in some years.

[0] Parallel Lisps / SPUR Lisp https://www.softwarepreservation.org/projects/LISP/parallel#... [1] SPUR Lisp: Design and Implementation https://www2.eecs.berkeley.edu/Pubs/TechRpts/1987/CSD-87-373... [2] Features for Multiprocessing in SPUR Lisp http://www2.eecs.berkeley.edu/Pubs/TechRpts/1988/CSD-88-406.... [3] Implementation of Multiprocessing SPUR Lisp http://www2.eecs.berkeley.edu/Pubs/TechRpts/1988/CSD-88-459....

Register Allocation in the SPUR Lisp Compiler: https://dl.acm.org/doi/pdf/10.1145/13310.13337

Design Decisions in SPUR: https://pages.cs.wisc.edu/~markhill/papers/computer86_spur.p...

SPUR: A VLSI Multiprocessor Workstation: https://www2.eecs.berkeley.edu/Pubs/TechRpts/1986/CSD-86-273...

Multiprocessing extensions in Spur Lisp: https://ieeexplore.ieee.org/document/31651

Opening up the topic: is there any work, nowadays, on making a CPU custom-made for one language, implementing in hardware some of its mechanisms? Or at least FPGA implementations?

Apart from CHERI extensions, and a few research papers on hardware-accelerated garbage collection (which I find super cool, and wonder why it isn't getting into actual production, given e.g. how stable Java GC is and how many huge companies use Java. Or maybe offloading it to an FPGA? The same way we have GPUs and TPUs for certain classes of computation?).

Greenarrays comes to mind, it's a Forth-oriented chip: https://news.ycombinator.com/item?id=23142322

The article doesn't really mention transputers, which were existent at the time and were remarkably similar in vision, with parallel multiprocessing, hardware network links, the Occam language, a ground-up Helios OS, and custom graphics card (Blossom, which would lead to the VGA standard).

Having a 3-element hardware stack somewhat restricts the use of languages on it - I imagine that Occam is similar to Forth in operation?

Occam looks like a concurrent Pascal, not like a Forth. The transputer had a 3-element hardware stack but it was used more like a cache for the workspace (kind of the memory stack). You can still find around the manual "Transputer Instruction Set - a compiler writer's guide" that explains how you would do that (section 5.3 Expression Evaluation).

Apart from Occam, there where C, C++ and Fortran compilers. Targeting the transputer is not more difficult than any other stack machine (like the JVM, the .Net CLR, CPython or Pascal p-code).

The weird/interesting thing about the transputer is that it is also an operating system: two task queues (high/low priority), preemptive scheduling and communication through channels (that can be one of the 4 serial ports or memory based).

Oh, thanks, I had forgotten about those crazy forthers (forthists? forthians?)!
They're not mainstream, but IBM's mainframes since Z14 have something called the Guarded Storage Facility which provides hardware assisted Java Garbage Collection[0], and Oracle's M8 has some hardware acceleration for Java "Streams"[1].

[0] https://www.ibm.com/support/pages/pause-less-garbage-collect...

[1] https://www.oracle.com/a/ocom/docs/sparc-t8-m8-server-archit...

Azul was actually selling servers with hardware-accelerated GC and other interesting features for Java: https://www.azul.com/newsroom/azul-systems-to-unveil-industr...
Then they found a way how to (ab)use amd64 MMU to do more or less the same thing that they had custom CPU architecture for (ie. GC barriers in hardware).
I heard (but don't know any details) that Apple M series processors have special instructions which are useful for objc_msgsend and other functions called frequently by Objective-C.
I remember someone mentioning an attempt to make all message passing in Smalltalk asynchronous running on separate threads, but I don't think anyone built a massively multithreaded CPU for that (besides, not that many messages are in flight at any given time, at least not on a desktop).
Could have meant RoarVM. https://github.com/smarr/RoarVM
There are projects for this, but right now Moore's law is still shuffling along well enough that by the time you'd get to market general processors will still be better, remembering that you need on the order of $1b to design a modern CPU.

Once it's truly dead pushing more features into silicon will the be only way we can get speedups and this will become a major research area again.

We could argue the same about highly parallel computations, yet discrete GPU cards exist. I am also old enough to remember the same thing attempted for physics in games, PhysX, which had its own cards (PPU) built by ASUS and a couple others.

There is also hardware acceleration for many audio and video codecs. "GC as a codec" doesn't strike me as something crazy: both are upgraded from time to time, but both are stable enough that hardware implementations are relevant over several years. Android phones would certainly benefit from it!

This approach got a bad reputation by the late 1980s, thanks to some spectacular failures like the iAPX 432. The death of the Lisp Machine market wasn't spectacular in the same way, but they weren't a commercial success either, and contributed to the sentiment that tailoring the hardware for specific languages wasn't the way forward.

You'll still find people making the case that we have continued to specialize computer hardware for a specific language, that language being C. They kind of have a point. I see it as more symbiotic than that: C caught on in large part because its abstract machine was a good fit for real hardware†, and that real hardware is the way it is because it's a Pareto-optimal way to do computation.

Fact is that most languages don't have a semantics which could be accelerated much in hardware. Take Java for an example: it's possible to implement the JVM as a chip, but then you have a stack machine, and you can't JIT it onto a register architecture.

What we do now is make the chip as fast as we can at doing the basic things a computer needs to do, and only that (this is the essence of RISC). That offloads making programs fast to compilers, which can do a better job of it if the instructions they're working with are very basic, and have a (reasonably) predictable duration and behavior. Itanium was the last serious attempt to disprove that thesis, and also failed rather spectacularly. The Mill is the latest contender, and well, I wish them luck.

That may not be the final word though, people should keep trying the "language on a chip" approach, and some still are. I have a hunch that Erlang semantics might be a good target for hardware-specific acceleration, there should be some degrees of freedom available from knowing that data is only shared between processes via a strict ABI. And just because implementing garbage collectors in hardware didn't really pay off in the 1980s doesn't mean that it's physically impossible to have a win with that approach. I'm just sketching out why you don't see that kind of thing much these days.

† C has been described as a "portable assembly language" and that has become steadily less true. That would be a stronger reading of my statement than I intended.

If you told an alien visitor from Vega—or a computer architect with a time machine from 1980—that an Nvidia GPU was built to be a language on a chip, I wonder what they’d infer about the nature of that language?

I don’t think they’d describe CUDA. Something more APL-like, I’d imagine.

EDIT: Maybe … https://dl.acm.org/doi/pdf/10.1145/319838.319870

> "tailoring the hardware for specific languages"

"hardware" in quotes. A bunch of the machines had no/little language specific hardware. For example an Xerox Interlisp-D machine was the same hardware like the Smalltalk or Mesa system. The microcode was different. The microcode provided the instruction set and then the machine would boot either into the corresponding operating systems for Interlisp, Smalltalk or Mesa. https://en.wikipedia.org/wiki/Xerox_Star

Similar for the MIT CADR and some others, it was also a microprogrammed 32bit CPU.

Symbolics' CPU were also microprogrammed, but they added hardware features to it.

The SPUR (which is a RISC chip) mentioned is a more generic design, but with support for languages features for Lisp. There were other chips in the making at that time, like the Symbolics Sunstone CPU, which was also a RISC design for Lisp, but which also did not reach the market.

> Take Java for an example: it's possible to implement the JVM as a chip, but then you have a stack machine, and you can't JIT it onto a register architecture.

One could do that, but it would be a more complex chip.

For Lisp CPUS "fast" for benchmarks was also not that much a goal. Goals oftenwere "fast" execution of a Lisp operating system (written in a dynamically typed and garbage collected language), support for large address spaces, support for Lisp data types&data representation, generic operations (like generic arithmetic operations) and compact machine code.

There is JOP [1] for Java.

[1] https://www.jopdesign.com/

I don't think the 'cdr' instruction would have created a new list.
Correct.

  (let* ((x '(a b c))
         (y (cdr x))
         (z (copy-list (cdr x))))
    (format t "y=~A, z=~A~%" y z)
    (setf (nth 1 x) 'w)
    (format t "y=~A, z=~A~%" y z))
=>

y=(B C), z=(B C)

y=(W C), z=(B C)

Just so you know, modifying quoted lists in Lisp is not dissimilar to setting string literals in C: it's undefined behavior, i.e., it's not guaranteed to work, or be portable if it does work, and it may or may not make your program behave strangely if it does work.

To make a mutable list, you could write (list a b c).

Yes, updating literals is asking for trouble, or at least can lead to some confusion.

If I replace ‘(a b c) in my example with (list ‘a ‘b ‘c) I got the same results.

FTA: “SPUR was ahead of its time in building a multiprocessor system in the mid-1980s. IBM’s POWER4 processor from 2001 was the first multicore microprocessor, with Intel and AMD each following four years later.”

That’s comparing apples with oranges. The POWER4 had two cores on a single die (https://en.wikipedia.org/wiki/POWER4) while in this system (FTA) “a processor would consist of three custom VLSI designs and around two hundred other chips”

Multiprocessing systems are much older, for example C.mmp (https://dl.acm.org/doi/10.1145/1480083.1480098, https://en.wikipedia.org/wiki/C.mmp) from 1971 (possibly also a bit of apples and oranges, but if so, IMO less so than in this article)