|
|
Log in / Subscribe / Register

Lisp and the foundations of computing

By Jake Edge
February 7, 2019

LCA

At the start of his linux.conf.au 2019 talk, Kristoffer Grönlund said that he would be taking attendees back 60 years or more. That is not quite to the dawn of computing history, but it is close—farther back than most of us were alive to remember. He encountered John McCarthy's famous Lisp paper [PDF] via Papers We Love and it led him to dig deeply into the Lisp world; he brought back a report for the LCA crowd.

Grönlund noted that this was his third LCA visit over the years. He was pleased that his 2017 LCA talk "Package managers all the way down" was written up in LWN. He also gave his "Everyone gets a pony!" talk at LCA 2018. He works for SUSE, which he thanked for sending him to the conference, but the company is not responsible for anything in the talk, he said with a grin.

More history than parentheses

His talk was based around the paper, but not restricted to it. Lisp itself was not really the focus either, so if attendees "were hoping to see tons of parentheses", they may be somewhat disappointed. After he read the paper, it led him to write a Lisp interpreter, which is a fairly common reaction for those who look at the language. In fact, he wrote four or five Lisp interpreters along the way.

[Kristoffer Grönlund]

$ sudo subscribe today

Subscribe today and elevate your LWN privileges. You’ll have access to all of LWN’s high-quality articles as soon as they’re published, and help support LWN in the process. Act now and you can start with a free trial subscription.

He started with the period of 1955 to 1958, when two MIT professors, McCarthy and Marvin Minsky, decided to start a new lab at the university. That was the genesis of the MIT Artificial Intelligence (AI) Lab. McCarthy coined the term "artificial intelligence" in that time frame; he was interested in teaching computers to think like humans.

Both of McCarthy's parents were communists and he grew up speaking Russian, Grönlund said. Much of McCarthy's early knowledge of math came from books in Russian that his parents had given him. That is interesting because much of the AI work that McCarthy participated in was done during the cold war and was often funded by various military-oriented organizations.

In the late 1950s, many believed that they were just on the cusp of having computers that could think like humans. The only obstacles foreseen were things like how to represent knowledge in a computer and how to get the computer to use reason and logic. Because of their mathematical backgrounds, the researchers believed that humans use logic, Grönlund said to scattered laughter. But in 2006, McCarthy gave a presentation entitled: "HUMAN-LEVEL AI IS HARDER THAN IT SEEMED IN 1955" (slides). After widespread laughter, Grönlund said that, unlike the beliefs in the 1950s, AI turned out to be really difficult.

It is interesting to contrast the attitudes toward AI in those early papers with what we are seeing today, he said. The advent of things like AlphaGo, self-driving cars, and other deep-learning applications has given rise to lots of optimism that "real AI" is just around the corner. But it may well turn out to still be really difficult.

Prior to 1956, all programming was done using assembly language. That changed with IPL, which was still assembly-based, but added features like list processing; IPL-II was cited by McCarthy as a big influence on Lisp. FORTRAN came about as the first high-level language in 1957. In 1958, McCarthy started to work on Lisp. Those advances came about in just a few years, which is amazing, Grönlund said.

In 1959, the AI lab got a computer, which was rather hard to do in those days. It was an IBM 704 and that specific model had a huge impact on the development of Lisp—both good and bad. These systems were multiple hulking gray boxes that Grönlund likened to refrigerators, with keypunch machines for creating punched cards that were fed into card readers and read into main memory. To get an idea of what that was like, he recommended a recent YouTube video that shows an IBM 1401 compiling and running a FORTRAN II program.

"Computers"

Investigating these old computers led him to the ENIAC Programmers Project. The ENIAC was one of the first computers; it was used to calculate trajectories for the military. Prior to that, during World War II, "computers" were also used for that purpose. Rooms full of women known as "computers" did those calculations by hand. When ENIAC was built, programmers were needed to configure and run it; six of the human computers from wartime were recruited to handle that task.

The task of programming was not considered to be difficult, since it was done by women, and these first programmers were not recognized as such. In the 1990s, the ENIAC Programmers Project was started and some of the women were tracked down and interviewed. There was a similar occurrence in Grönlund's native Sweden: the first computer was programmed by a woman, Elsa-Karin Boestad-Nilsson, who is largely unknown, even in Sweden, he said.

As he was researching and encountering all of these interesting computing pioneers, he ran into another that took him back to McCarthy. Vera Watson was of Chinese-Russian descent and was hired by IBM for a machine-translation project because she spoke Russian, "but she turned out to be a really good programmer". She eventually married McCarthy. In her spare time, Watson was an accomplished mountaineer and was part of an all-woman expedition to climb Annapurna I in 1978, where she unfortunately lost her life.

When Grönlund looked at the first Lisp programming manual [PDF], which was published March 1, 1960, he saw the name of Phyllis Fox listed as one of the authors. It turns out that Fox was a human computer during the war and went on to create the first simulation language, DYNAMO, which was used to simulate various societal growth scenarios in a study called "The Limits to Growth". The simulation found three possible outcomes, two of which showed a societal collapse in the mid-late 21st century, while the other resulted in a stable world. "So we're not doomed, we have a one-in-three chance that things are going to work out", he said with a laugh.

He noted that the authors of Lisp that were listed in the manual included McCarthy, Fox, and a number of students. The only one of those who had any experience in writing a computer language was Fox, but she is only credited with writing the manual itself in the acknowledgments section. Grönlund said that he didn't have any proof, but that he thought that maybe there was "something fishy going on there". Fox went on to work at Bell Labs on various projects, including two different numerical libraries.

Back to Lisp

McCarthy thought that the classic Turing machine was far too complicated to be used in papers on computability and the like. So he wanted to come up with a way to represent computation in a way that computers could use directly. He felt that the Turing machine, with its infinite tape, read/write head, and so on, was more like a physical device and not particularly mathematical. He wanted a mathematical notation for working with programs, which is where Lisp came from.

McCarthy wanted to show that Lisp was a superior way to describe computation; he thought that the best way to do that was to create the "universal Lisp function". That function would take a Lisp program as its argument and execute the program. He came up with the eval function, which required a notation for representing Lisp functions as data. He never really intended for Lisp to be a programming language, it was simply superior notation for the paper he was working on.

One of McCarthy's graduate students, Steve Russell, who had been hand-compiling code into machine code all day, recognized that implementing the universal function would make things a lot easier. He suggested that he write eval to McCarthy, who said: "ho, ho, you're confusing theory with practice, this eval is intended for reading, not for computing". But, then, "he went ahead and did it", McCarthy said (as quoted by Grönlund).

The syntax of Lisp is inspired by the "Lambda calculus" notation that was developed by Alonzo Church in the 1930s. Both are based on the idea that any kind of computation can be expressed as function applications. The result is a "syntax that has a lot of parentheses". He quoted from the Lisp 1.5 Programmer's Manual, which recommended ending Lisp card decks with "STOP followed by a large number of right parentheses" so that a programming error would not cause the interpreter to continue reading indefinitely. It is clear from this that the parenthesis problem was with Lisp from the early days.

At its most basic level, Lisp programs contain two things: atoms and lists. Atoms are symbols, while lists contain atoms or other lists. So, the following contains an atom, a list of four atoms, and an empty list:

    foo
    (a b c d)
    ()
A more complicated list is below, it has two elements, each of which is a list of three atoms:
    ( (a b c) (d e f) )
Functions are invoked via lists, with the function name as the first atom and the remainder of the list as arguments, so f(x) would be:
    (f x)
That leads to a problem when you want to refer to a list as simply data, rather than as a function invocation. In Lisp, there is the idea of a quote function (though ' is often used as a shortcut) that can be used as follows ("=>" will be used to show the result):
    (quote a) => a
    (quote (a b c)) => (a b c)
    '(a b c) => (a b c)
There are various dialects of Lisp that have been used over the years, including Scheme, which is what Grönlund used for his examples.

The influence of the IBM 704 can be seen in the next example. Lists have traditionally been represented as linked lists in the interpreter. Two of the primitive operations, car and cdr, take their names from operations on the IBM 704. car is the "contents of the address part of the register", while cdr is the "contents of the decrement part of the register", he said. The upshot is that car results in the first element of its list argument, while cdr results in the rest of the list:

    (car '(a b c)) => a
    (cdr '(a b c)) => (b c)
Another primitive operation is cons, which constructs a list from its two arguments:
    (cons 'a '(b c)) => (a b c)

Building new functions in Lisp is done with the lambda function. It takes a list of arguments and the computation to be done:

    (lambda (x) (* x 2))
    ((lambda (x) (* x 2)) 4) => 8
The first line simply defines a function that multiplies its argument by 2. He pointed out that even arithmetic operations are done using function notation, rather than using infix expressions as in other languages. The second line actually uses the defined function with the argument 4. He also introduced the cond function, which acts as a conditional branch. It evaluates its arguments (each of which consists of a test and an action), finding the first test that evaluates to true and executing the associated action.

Even though FORTRAN came out the previous year, Grönlund said, Lisp had the first implementation of conditionals. The first version of FORTRAN could do a GOTO based on whether a value was zero or non-zero, but that was because the inventors of the language were still thinking in terms of machine language, he said. One of the big innovations of Lisp was that it allowed arbitrary tests in an if-then-else kind of construct.

His description of the language, which is abbreviated somewhat here, is enough to create a fully functioning version of Lisp. There are lots of pieces that can be added for convenience, such as mathematical operations, but that aren't truly needed in order to build a Lisp that is Turing complete. On page 13 of the Lisp 1.5 manual that was linked above, you can find the eval function that McCarthy wrote (though the notation is different than Lisp). Grönlund quoted Smalltalk inventor Alan Kay on the significance of that:

Yes, that was the big revelation to me when I was in graduate school—when I finally understood that the half page of code on the bottom of page 13 of the Lisp 1.5 manual was Lisp in itself. These were "Maxwell's Equations of Software"! This is the whole world of programming in a few lines that I can put my hand over.

Code and data

One of the other major innovations of Lisp was in showing that there is no real distinction between code and data. There is no sharp boundary between data formats and code formats. Even though this was recognized in 1960, we still fail to completely understand it today, Grönlund said. Some security vulnerabilities come about because programmers do not recognize that passing data through a program is often also just passing code through it. Handling code as data is something that Lisp got right.

Grönlund is not fond of XML, but it is okay for use on data. The problem is that when you have some kind of large configuration file in XML, parts of it will be more "code-y"; those parts will look awful in XML, he said. He works a lot with the Pacemaker project, which has the concept of "rule expressions", which are "horrible" because they are code written in XML.

To be honest, he doesn't think that Lisp as a language itself is all that interesting today. There are various dialects and descendants still in use, however. There are also some applications of Lisp that are interesting today, he said, including Guix, which is a transaction-based packaging system that uses an implementation of Scheme: Guile.

Most people don't use Lisp today, but many of its ideas survive. An interpreter is something new that Lisp brought with it; today interpreters are commonplace. Similarly, garbage collection was a concept that was a Lisp innovation. Many of the people who worked on Lisp, especially Scheme, went on to work on Java so elements of Lisp pop up there. JavaScript, Perl, Ruby, and Python are all Lisps without "the parenthesized syntax", he said.

Lisp advocates will claim that the parenthesis problem is something that people can get used to, but Grönlund thinks it is probably just too much of a hassle for most. Given what he has said, he wondered "Why Lisp?" That elicited some laughter from attendees (and Grönlund himself), but Lisp is something he's been attracted to recently and he wanted to try to understand why that was.

He started thinking about it in terms of his attraction to Linux and open source because he believes the two are related. The open-source community values its connection to the past. A community can choose to value innovation, intelligence, and entrepreneurial spirit as the highest ideals, or it can value wisdom, craft, and perfecting something by building on the efforts of others. When you consider wisdom and craft, he said, sharing obviously falls out of that; you want to learn from those who came before and to teach those who come after in order to help hone the craft. That's the connection that he sees; in free software, we are building on this legacy that goes all the way back to the first computers and first programmers.

If you look at proprietary software, he said, it is breaking that history. It is taking the chain of legacy, sharing, and history and breaking it off for selfish purposes; it is anti-social. "I don't like that at all", he said to applause.

He referred to a talk earlier in the week that advocated teaching assembly language to students so that they can understand what the computer is really doing. He thinks teaching Lisp is at least as important, even though he didn't like it or really understand its significance when he had to learn it at his university. Lisp teaches the fundamentals of computing and of computability. You can look at the eval function and have the whole concept of computing in a single screen of code.

While the idea of free software was new in some ways when Richard Stallman came up with it, it really was defending an old concept of sharing and building on the knowledge of others instead of taking ideas and not sharing anymore. Free software is based on a culture building on itself; it is proprietary software that is breaking this chain. His overarching message here was that maintaining the connection to our shared past is an important and worthy goal.

And, of course, he ended his talk with a slide reading:

    STOP )))))))))))
That led to much applause and laughter.

A video in WebM format of the talk is available, as is a YouTube version.

[I would like to thank LWN's travel sponsor, the Linux Foundation, for travel assistance to Christchurch for linux.conf.au.]

Index entries for this article
Conferencelinux.conf.au/2019


to post comments

Readable Lisp

Posted Feb 7, 2019 15:50 UTC (Thu) by david.a.wheeler (subscriber, #72896) [Link]

If you're interested in Lisp but want a more "readable" version, check out Readable Lisp, which provides notations designed to make Lisp code easier to read. There's a video, documentation, and open source software implementations.

Lisp and the foundations of computing

Posted Feb 7, 2019 16:23 UTC (Thu) by willy (subscriber, #9762) [Link] (4 responses)

I have a feeling there's some deep connection between Haskell Monads and Lisp's eval function, but I don't really understand either well enough to know if this feeling has anything to back it up or not.

Lisp and the foundations of computing

Posted Feb 7, 2019 22:24 UTC (Thu) by nybble41 (subscriber, #55106) [Link] (3 responses)

Monads are a simple concept at heart, despite all the blog posts on the subject. They consist of a category with identity and composition within some context, typically referred to as "f" (for Functor) or "m" (for Monad):

id :: a -> a -- identity function
pure :: a -> f a

(.) :: (b -> c) -> (a -> b) -> (a -> c) -- function composition
(<=<) :: (b -> m c) -> (a -> m b) -> (a -> m c)

plus the ability to lift a pure function to work on values inside the context:

fmap :: (a -> b) -> (f a -> f b)

and the ability to apply a value in a context to a function in the same context:

($) :: (a -> b) -> a -> b -- function application
(<*>) :: f (a -> b) -> f a -> f b

Elsewhere you may see "return" or "unit" rather than "pure", and (>>=) or "join" in place of (<=<); these are merely different ways to express the same capabilities. I chose this formulation to make the parallels with the non-monadic versions more obvious. The "fmap" operation is available for all functors, and "pure" and (<*>) work for all applicative functors. The ability to compose functions within the context (<=<) is what distinguishes a monad.

The type of expressions in most other (non-pure) languages would be written as "IO a" in Haskell; this is the context in which you can perform any kind of I/O, including reading and writing global variables, accessing files, or communicating over a network, with a result of type "a". Since this is the *only* context these languages natively support—there are no contexts where I/O is not permitted—they don't bother to mention it: "f a" becomes "a", (<=<) becomes simple function composition, and "pure", "fmap", and (<*>) all turn into plain identity functions (specialized to functions "a -> b" in the latter two cases).

The List "eval" function takes a syntax tree as input and evaluates it in the standard I/O context. There is no particular connection to monads beyond in the general sense that all languages which permit the composition of I/O actions (i.e. almost all of them) have an underlying monadic structure. Haskell is not unique in its use of monads; the significance is that monads in Haskell are *explicit*, and you can construct contexts which do *not* permit arbitrary I/O.

Monads

Posted Feb 8, 2019 6:38 UTC (Fri) by CChittleborough (subscriber, #60775) [Link] (2 responses)

A crude but possibly-helpful way to think of monads is that they are accumulations of changes of state. So Haskell's IO monad kinda sorta represents the I/O performed by the program. This turns out to fit nicely with Haskell's lazy evaluation.

Monads

Posted Feb 8, 2019 18:29 UTC (Fri) by nybble41 (subscriber, #55106) [Link]

> A crude but possibly-helpful way to think of monads is that they are accumulations of changes of state.

There are two problem with that. To begin with, there are monads like Reader which provide a context but don't permit any changes in state. I'm not certain it's helpful to think of the Cont type (continuations) as an accumulation of state, either, though it can include that. You can also accumulate changes in state without monads; an applicative functor is sufficient:

GHCi> runState (modify (+2) *> get <* put 99) 7 -- only requires Applicative, not Monad
(9,99)
GHCi> runState (get >>= \state -> put (state+2) *> get <* put 99) 7 -- requires Monad for (>>=)
(9,99)

The difference is that in the first case the first action is a constant "apply a predetermined function to the state", while in the second case a "set the state to a specific value" action is generated on-the-fly as a function of the previous state, which requires a monad. Now for State this distinction is somewhat artificial, since any combination of "get" and "put" could also be expressed using "modify", but not every monad is so accommodating. Without the Monad interface for IO, for example, it would be impossible for a Haskell program's output to depend on its input (setting aside specialized library functions like "interact" which are themselves implemented in terms of the Monad operators).

Monads

Posted Feb 14, 2019 18:02 UTC (Thu) by HelloWorld (guest, #56129) [Link]

Huh? Monads and monadic I/O work just fine without lazy evaluation. I do it every day.

Lisp and the foundations of computing

Posted Feb 7, 2019 20:08 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link] (16 responses)

"Garbage collection" wasn't exactly "a LISP innovation", more "a LISP makeshift". McCarthy didn't want to use "manual erasure", that is, explicit memory management a la malloc/ free. He also wanted list tail structure. But he couldn't use reference counts because there was no place to put them in IBM 704 registers when considering how the already existing code used these. Lastly, he didn't want to deal with the issue at all as "only toy examples were being done". Implementation of LISP memory management was thus postponed until the need arose and then offloaded to some student based on the 'clever' observation that all pointers to objects in IBM 704 memory must necessarily exist in IBM 704 memory, all of which was accessible to the LISP runtime.

To this date, automatic resource management is thus being held back by hardware limitations of IBM 704 computers and almost universally wrong assumptions based on particulars of the IBM 704 architecture.

Lisp and the foundations of computing

Posted Feb 8, 2019 7:40 UTC (Fri) by epa (subscriber, #39769) [Link] (4 responses)

Are you saying that reference counting is a better way of managing memory than mark-and-sweep garbage collection? Surely it’s not enough by itself because of circular references.

Reference Counting

Posted Feb 8, 2019 13:27 UTC (Fri) by skitching (guest, #36856) [Link] (2 responses)

Python (at least the standard CPython interpreter) successfully uses reference-counting plus cycle-detection. So does Perl.

Refcounting has weaknesses, including requiring lots of memory-updates - which in turn means copy-on-write memory pages don't work well (at least on systems where the refcount is colocated with the data). And in multithreaded apps, there is contention on the refcount fields. But for non-forked non-threaded code it is fast and simple (even with cycle-detection added).

Reference Counting

Posted Feb 10, 2019 12:22 UTC (Sun) by epa (subscriber, #39769) [Link] (1 responses)

AFAIK Perl 5 does not detect circular references. You have to break the cycle manually or use weak references (provided by a library). Otherwise the memory will leak.

Reference Counting

Posted Feb 11, 2019 19:45 UTC (Mon) by rweikusat2 (subscriber, #117920) [Link]

This is mostly correct: Weak reference support is provided via Scalar::Util module but this is part of the core Perl distribution and just provides access to some builtin functions which "people have expressed would be nice to have in the perl core, but the usage would not really be high enough to warrant the use of a keyword". A weak reference will be set to undef if the object it was referring to is destroyed. Reference cycles are sufficiently rare that this isn't much of a burden and weak references are easy enough to use.

Lisp and the foundations of computing

Posted Feb 8, 2019 22:37 UTC (Fri) by rweikusat2 (subscriber, #117920) [Link]

Mostly, I was just paraphrasing McCarthy's "History of LISP" paper, eg
The erasure problem also had to be considered, and it was clearly un- aesthetic to use explicit erasure as did IPL. There were two alternatives. The first was to erase the old contents of a program variable whenever it was updated. Since the car and cdr operations were not to copy structure, merging list structure would occur, and erasure would require a system of reference counts. Since there were only six bits left in a word, and these were in separated parts of the word, reference counts seemed infeasible without a drastic change in the way list structures were represented.

The second alternative is garbage collection in which storage is abandoned until the free storage list is exhausted, the storage accessible from program variables and the stack is marked, and the unmarked storage is made into a new free storage list. Once we decided on garbage collection, its actual implementation could be postponed, because only toy examples were being done.

This second alternative can only work if live pointers cannot exist anywhere where the runtime can't find them. One example where this isn't true is the epoll system call which can keep an application state pointer in kernel memory. Another drawback is that there's no good way to extend this system to manage other exhaustible resources than memory, eg, open files, or just other things with a acquire/ relaese semantic (like mutexes, for instance).

Lisp and the foundations of computing

Posted Feb 10, 2019 22:27 UTC (Sun) by ncm (guest, #165) [Link] (10 responses)

Jim Backus used to call null pointers his "billion-dollar mistake", but arguably garbage collection -- more specifically, obligate garbage collection -- has held programming back in many worse ways.

To this day, it is not possible to get one's programming language idea taken seriously within academia unless it has drunk the GC kool-aid. So, new languages from academia are doomed from the outset. As a consequence, new programming languages usable industrially never originate in academia, and important theoretical ideas are adapted piecemeal into C++, where they can be at all.

There has been plenty of time for a language to arise to supplant C++. Its stagnation until 2011 opened the door. What do we have? Rust is the only serious contender, not begun until after 2011, and not academic. C++ is evolving rapidly, now, making it harder to supplant, and, increasingly, Rust is weighed down by its own legacy choices.

Lisp and the foundations of computing

Posted Feb 10, 2019 23:59 UTC (Sun) by Cyberax (✭ supporter ✭, #52523) [Link] (7 responses)

> As a consequence, new programming languages usable industrially never originate in academia, and important theoretical ideas are adapted piecemeal into C++, where they can be at all.
Seriously? The most popular modern languages all use GC. Heck, C++ and C are basically the only exceptions.

Lisp and the foundations of computing

Posted Feb 11, 2019 15:26 UTC (Mon) by rweikusat2 (subscriber, #117920) [Link] (6 responses)

And that's the precise reason why I keep writing a lot of Perl 5 code. I don't care for yet another "exciting syntax experiment" running atop the JVM or Someone's Very Own Version of That[tm]. I want extensible, automatic managment of everything which has to be released if I can get that. This is a huge work saver and prevents all kinds of delayed-action runtime timebombs. Deterministic finalization of objects is also very useful.

Even when ignoring the practical problems, there's still the theoretical concern that the assumption that pointers to live objects cannot exist anywhere where the garbage collector can't access them is simply wrong: pointers are just data. And "But it's immensely popular with a vanishingly small subset of the population of this planet" doesn't change that. People like to believe in all kinds of funky stuff.

Lisp and the foundations of computing

Posted Feb 11, 2019 20:45 UTC (Mon) by NAR (subscriber, #1313) [Link] (2 responses)

A non-trivial part of the telephone infrastructure of the world runs partially on Erlang, which has GC and soft-realtime. Just because JVM has bad characteristics, don't expect all other VMs share this problem.

Lisp and the foundations of computing

Posted Feb 11, 2019 22:58 UTC (Mon) by rweikusat2 (subscriber, #117920) [Link]

Erlang certainly doesn't belong to the set of "popular modern languages" this statement was referring to. And the points I raised are conceptual issues/ properties of tracing collectors regardless of implementation.

Citation needed

Posted Feb 12, 2019 6:26 UTC (Tue) by renox (guest, #23785) [Link]

> A non-trivial part of the telephone infrastructure of the world runs partially on Erlang

Are you sure about that? I thought that Ericsson switched to C++.

That said Erlang was designed for this use case.

Lisp and the foundations of computing

Posted Feb 12, 2019 8:05 UTC (Tue) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

> And that's the precise reason why I keep writing a lot of Perl 5 code.
Perl 5 is typically used in short-lived processes where memory leaks don't bother anybody. Long-lived Perl 5 code is a recipe for a disaster. And I actually know a case where it literally led to a disaster with several people ending up in a hospital.

> Even when ignoring the practical problems, there's still the theoretical concern that the assumption that pointers to live objects cannot exist anywhere where the garbage collector can't access them is simply wrong: pointers are just data.
First, plenty of architectures have tagged memory so pointers are explicitly treated NOT as data. Even Intel tried to move that way with MPX.
Second, simply not exposing the raw pointer arithmetic to the underlying language is enough so that compiler/runtime can always enumerate all the live pointers.

In practice it turned out that modern GCs are rapidly becoming better than even the best manual memory management. For example, this led to a completely counter-intuitive popularity of Java in high-speed trading, Azul JVM can hard-guarantee that there are no pauses: https://www.azul.com/products/zing/pgc/

These advances are still percolating to the wider audience, but the writing on the wall is pretty clear.

Lisp and the foundations of computing

Posted Feb 12, 2019 16:17 UTC (Tue) by rweikusat2 (subscriber, #117920) [Link] (1 responses)

>> And that's the precise reason why I keep writing a lot of Perl 5 code.
> Perl 5 is typically used in short-lived processes where memory leaks don't bother anybody. Long-lived Perl 5 code is a recipe for a
> disaster.

Even short-lived processes may sometimes need a lot of memory (or file descriptors). As I already mentioned, there are also other kinds of resources where "exhaust and [try to] recover from that" often cannot be used (locks). As opposed to "long-lived Java processes" which are a recipe for "club to death with a blunt instrument and restart" (to free up memory), perl handles long-lived processes just fine. As opposed to a popular superstition, programming languages don't write code, programmers do :-). Perl isn't particularly concerned with forcing people to code sensibly and combined with the mindset which begat the gets-using fingerd, this is a recipe for disaster. But the disaster is duly going to be repated in any other kind of programming language as "human ingenuity" can beat any kind of automated safety measure.

> First, plenty of architectures have tagged memory so pointers are explicitly treated NOT as data. Even Intel tried to move
> that way with MPX.

"Plenty of recent CPU designs" have some sort of support for "tagged pointers" because it's conjectured that this will be useful for something (in addition to making certain people less uncomfortable). Whether this will lead to anything in the real world remains to be seen. On mainstream Linux (etc) pointers are 'just data'.

> Second, simply not exposing the raw pointer arithmetic to the underlying language is enough so that compiler/runtime can
> always enumerate all the live pointers.

It's always possible to write another IBM 704 emulator in software. But why?

Lisp and the foundations of computing

Posted Feb 13, 2019 1:00 UTC (Wed) by Cyberax (✭ supporter ✭, #52523) [Link]

> Even short-lived processes may sometimes need a lot of memory (or file descriptors). As I already mentioned, there are also other kinds of resources where "exhaust and [try to] recover from that" often cannot be used (locks).
That's why most languages these days have some kind of try-with-resource blocks: "with" in Python, "defer" in Go, "try(...)" in Java and so on. And unlike the leak-prone Perl code it works reliably.

> perl handles long-lived processes just fine
Yeah, by leaking tons of memory and eventually crashing.

> "Plenty of recent CPU designs" have some sort of support for "tagged pointers" because it's conjectured that this will be useful for something (in addition to making certain people less uncomfortable).
These designs are definitely more secure, they eliminate possibility for a whole class of vulnerabilities. Unfortunately, gung-ho C/C++ programmers have contaminated pretty much everything with unsafe pointer arithmetic crap that can't utilize tagged memory efficiently.

> It's always possible to write another IBM 704 emulator in software. But why?
Because GC is more reliable than refcounting done by gung-ho programmers.

Lisp and the foundations of computing

Posted Feb 11, 2019 9:54 UTC (Mon) by epa (subscriber, #39769) [Link]

I thought it was Tony Hoare who said that? https://www.infoq.com/presentations/Null-References-The-B...

Lisp and the foundations of computing

Posted Feb 11, 2019 14:47 UTC (Mon) by peter-b (guest, #66996) [Link]

> Rust is weighed down by its own legacy choices.

Which particular choices do you feel are weighing Rust down?

Lisp and the foundations of computing

Posted Feb 7, 2019 20:33 UTC (Thu) by klossner (subscriber, #30046) [Link] (2 responses)

At its most basic level, Lisp programs contain two things: atoms and lists.
No, the two things are atoms and "conses" (dotted pairs): (a . b)
Lists have traditionally been represented as linked lists in the interpreter.
The language defines lists to be linked. The list notation (a b c) is shorthand for (a . (b . (c . nil))): each cell consists of one list element and a pointer to the rest of the list.
The first version of FORTRAN could do a GOTO based on whether a value was zero or non-zero.
It was a three-way branch depending on whether the value was negative, zero, or positive. Statement labels were integers, so
        IF (value) 10, 20, 30
would branch to one of three statements depending on value.
(I used to punch statements like that onto cards to be fed into a hopper, mid-twentieth century.)

Lisp and the foundations of computing

Posted Feb 7, 2019 21:03 UTC (Thu) by ejr (subscriber, #51652) [Link]

Where did I leave my walker? (aka, me too)

Lisp and the foundations of computing

Posted Feb 8, 2019 15:30 UTC (Fri) by krig (guest, #92101) [Link]

Thank you for correcting me on these things! I should have prefixed the whole thing with a disclaimer that my start with computers was with the Commodore 64, and I never saw punch cards in action personally :)

Regarding the statement on Lisps traditionally using linked lists - the reason why I qualified it that way is because of Clojure, which doesn't build on conses or linked lists and has its own immutable data structures as the building blocks (which leads to some other interesting departures from "normal" lisps).

Lisp and the foundations of computing

Posted Feb 8, 2019 4:15 UTC (Fri) by marcH (subscriber, #57642) [Link] (20 responses)

> There is no sharp boundary between data formats and code formats.
> Even though this was recognized in 1960, we still fail to completely understand it today, Grönlund said. Some security vulnerabilities come about because programmers do not recognize that passing data through a program is often also just passing code through it.
> Handling code as data is something that Lisp got right.

Agree about the lack of recognition and the consequences but this is overly simplistic. Drawing lines between code and data can also help with security; for instance blocking execution of heap and stack, not trusting inputs, etc. Basically guarding against those same errors.

> Lisp is something he's been attracted to recently and he wanted to try to understand why that was.

One specificity of Lisp languages is indeed the (unsafe?) lack of boundary between code and date hence the extremely easy way to generate new code _at run-time_.

This is the exact same power that also has a great potential (even greater than Perl?) to end-up with "write-only" programs. This can dwarf parentheses in terms of readability. Security aside, the distinction between code and data is a useful design and communication technique.

Lisp and the foundations of computing

Posted Feb 8, 2019 9:04 UTC (Fri) by spacefrogg (subscriber, #119608) [Link] (19 responses)

This is not how I understand the article (or understand Lisp). Lisp teaches you that there is no boundary between code and data. This is an observation, not a design decision of a programming language. The same goes for eval. Lisp is just making the existence of the "concept of eval" obvious. It was there all along and is there in every Turing-complete language.

What you discredited as "(unsafe?) lack of boundary" is a consequence of Lisp making these two concepts so obvious and accessible. This said, unless a programmer understands that eval exists in every programming language and that there is no distinction between code and data, these people will write unsafe programs.

It does not make sense to attack the language for stating obvious truths. The same way that you would not want computers to be less powerful because most of humanity uses them to their personal or social disadvantage. (But maybe, we should...)

I don't mean to discredit your observations. I just don't think the conclusions, you draw from them, are correct. Teach people to wield the power instead of giving them broken but "safer" languages.

> Security aside, the distinction between code and data is a useful design and communication technique.
To this end, I cannot agree with this statement. This is the source of most of our problems, functionally and security-wise, not the solution. We as programmers must bridge the mental gap, in that non-tech people think, that there is a natural distinction between code and data when there is not. Programmers must enforce the distinction (as much as possible) between code and data by restricting the complexity of everything that is to be considered "data". Otherwise it will "come to life" very much like Frankenstein's monster, underspecified, unsafe for the public and not to be understood.

Lisp and the foundations of computing

Posted Feb 8, 2019 17:04 UTC (Fri) by marcH (subscriber, #57642) [Link] (17 responses)

> Lisp teaches you that there is no boundary between code and data. Lisp is just making the existence of the "concept of eval" obvious. It was there all along and is there in every Turing-complete language.

You're very much confusing science and engineering (or trolling, or both)

> Lisp making these two concepts so obvious and accessible.

A little bit of an understatement. Even for shell scripting which is probably not far from a second best, "eval" is not exactly a walk in the park: http://resources.mpi-inf.mpg.de/departments/rg1/teaching/...

I've used "eval" a number of times in shell scripts yet I would... probably not let it pass code review. I guess this makes me a dangerous dictator or something, see below :-)

> This said, unless a programmer understands that eval exists in every programming language...

Short of embedding the C compiler in every program, please show us how "eval" works in C.

> attack the language for stating obvious truths [...] The same way that you would not want computers to be less powerful because most of humanity uses them to their personal or social disadvantage. [...] Teach people to wield the power instead of giving them broken but "safer" languages.

Ha! Sorry I totally missed the political aspects of Lisp, maybe I was too busy with engineering and a bit of science :-)

If this actually matters to you then you should really think bigger than this comments section and (for instance) reason the security experts who put a lot of effort to enforce better boundaries between code and data at the *hardware* level: https://lwn.net/Articles/758245/

Lisp and the foundations of computing

Posted Feb 8, 2019 20:46 UTC (Fri) by spacefrogg (subscriber, #119608) [Link]

I am sorry that I was unable to convey my message. I am totally on your side when it comes to "best practices" in computer programming. I was neither advocating for the use of "eval" nor for writing programs that modify themselves during execution (thus treating some input as code). Quite the opposite, actually.

"accessible" was merely meant as "to give the programmer an in-language access to the interpreter itself", not as "is easy to use".

> Short of embedding the C compiler in every program, please show us how "eval" works in C.

"eval" is the concept of an execution model that reads your input data and makes the computer do things. I didn't say that "eval" in C must implement the execution model and input language of C itself. What I wanted to express was, if you're not careful, your data protocol becomes so complex, that you accidentally implement a Turing-complete interpreter inside your program that processes your data. This thing implements the concept of "eval", whether you give it a name or not. Doing this without knowing is dangerous. And it definitely is done needlessly and too often.

> Ha! Sorry I totally missed the political aspects of Lisp, maybe I was too busy with engineering and a bit of science :-)

I'm sorry to hear that, but I fail to see how security aspects are anything but the political side of software development.

> If this actually matters to you then you should really think bigger than this comments section and (for instance) reason the security experts who put a lot of effort to enforce better boundaries between code and data at the *hardware* level: https://lwn.net/Articles/758245/

You're right and I don't actually like that development too much. Just to bring a counter example: The x86's MMU was discovered to be accidentally Turing complete. This is what happens when you add complexity without thinking hard enough. Holds true for hardware and software alike.

Lisp and the foundations of computing

Posted Feb 9, 2019 21:06 UTC (Sat) by nix (subscriber, #2304) [Link] (14 responses)

> > This said, unless a programmer understands that eval exists in every programming language...
> Short of embedding the C compiler in every program, please show us how "eval" works in C.

That *is* how "eval" works in C. GCC and execve() are a C implementation of "eval". Any language in which you can implement a compiler or an interpreter is a language in which eval can necessarily be implemented for any other language (ignoring toy languages with ridiculous restrictions on program size or other limitations preventing anything remotely resembling "approximate Turing-completeness").

(And *still* people miss it, as you did here.)

Sure, GCC is complex. Nobody said that eval had to be *easy* to implement, or that all eval implementations had to be trivial: it varies with the language being evaluated -- though Lisp eval is much simpler than most.

Lisp and the foundations of computing

Posted Feb 9, 2019 21:16 UTC (Sat) by marcH (subscriber, #57642) [Link] (3 responses)

Funny enough execve() is part of POSIX, not part of C and not always available.

You can invoke anything with execve() so yeah sure you can invoke a C compiler - *if* there is one. Who does that in practice? Only compilers using C as an intermediate and low-level language.

> it varies with the language being evaluated -- though Lisp eval is much simpler than most.

Again a understatement confusing science with engineering.

Lisp and the foundations of computing

Posted Feb 9, 2019 21:55 UTC (Sat) by karkhaz (subscriber, #99844) [Link] (1 responses)

I don't agree with your point, but here's some work presented at the LLVM Developers' Meeting about having eval() in C++:

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

Summary: the speaker is a developer of LLDB, and in debuggers you need the ability to eval() at least a restricted subset of the language (for example, the user might want to print the value of some expression, involving member lookup and pointer indirection, so you need to be able to compile that fragment and eval it in the context of the paused program). He talked about what parts of LLDB you could use if you wanted to implement eval in C++.

Lisp and the foundations of computing

Posted Feb 10, 2019 1:03 UTC (Sun) by marcH (subscriber, #57642) [Link]

Interesting!

PS: of course I don't understand why they waste time in developers' meetings discussing this when they could just have read this comments section and discovered it's been there all along :-)

Lisp and the foundations of computing

Posted Feb 9, 2019 22:09 UTC (Sat) by nix (subscriber, #2304) [Link]

> You can invoke anything with execve() so yeah sure you can invoke a C compiler - *if* there is one. Who does that in practice? Only compilers using C as an intermediate and low-level language.

My point was not execve() in particular: merely that all you need is a way to compile data into something executable and a way to execute that thing. In POSIX that happens to be called execve(), but *obviously* that's not the only possible option!

Lisp and the foundations of computing

Posted Feb 10, 2019 0:53 UTC (Sun) by mgb (guest, #3226) [Link] (9 responses)

> GCC and execve() are a C implementation of "eval".

A new program is not the same as eval in the context of the current process.

Even loading a new object file as a shared library into the current process doesn't get you to eval.

Lisp and the foundations of computing

Posted Feb 10, 2019 1:20 UTC (Sun) by marcH (subscriber, #57642) [Link]

There's probably some ivory tower where it's the same.

Lisp and the foundations of computing

Posted Feb 11, 2019 0:29 UTC (Mon) by nix (subscriber, #2304) [Link] (7 responses)

Well generate a shared library and do a dlopen() then. Or generate bytecode out of the C code and interpret it. These are all things you can do in C, or in any non-cripppled language.

(I note that everyone is picking nits like mad to avoid engaging with the actual point here. C is not magic nor is it impossible to implement 'eval' in. It just depends on how precisely you define it. Now Lisp macros, on the other hand, *that* is syntactic and is quite beyond C.)

Lisp and the foundations of computing

Posted Feb 11, 2019 1:17 UTC (Mon) by marcH (subscriber, #57642) [Link] (6 responses)

> Well generate X and do Y then. These are all things you can do in C, or in any non-cripppled language. [...] C is not magic nor is it impossible to implement 'eval' in. It "just" depends on how precisely you define it.

Science is about all the wild things that are possible given infinite time, knowledge and resources. From a very theoretical perspective C and Lisp are probably the same.

Engineering is about the practical things that people actually do and use given real-world time, knowledge and resources. From a practical perspective C couldn't be more different from Lisp for many reasons (not just C's unsafe boundaries between code and data) hence no one ever tried to actually implement in C anything equivalent to Lisp's "eval" because even if theoretically possible that would one of the most futile and pointless things ever. As useful as trying to make a screwdriver out of a shovel.

Of course science makes better engineers and knowing concepts and ideas from other (higher-level and more abstract...) languages like "eval" most definitely helps making better and safer programs in any (typically: lower-level) language. This helps precisely because some languages do _not_ have any practical "eval" feature for real-world usage.

Thanks for the conference report and fun WE discussion about computer science and eval. Now on Monday, back to $DAYJOB engineering and yet another attempt to explain in <random code review> why copy/paste is bad and "updated stuff" not a good commit message :-)

PS:
- if you're interested in seeing how some science can be applied to practical engineering and shaping a better C then check out Rust.
- off-topic but another reason why C is more and more obsolete: performance disconnect on modern hardware https://queue.acm.org/detail.cfm?id=3212479 (great paper)

Lisp and the foundations of computing

Posted Feb 11, 2019 7:22 UTC (Mon) by amacater (subscriber, #790) [Link]

I really need to steal your last paragraph: it's so true.

$DAYJOB is what keeps most of us running to pay for housing, family, social life, food - but the disconnect between that and LWN is annoying. LWN, the Beowulf list and my Free Software friends and colleagues are, however, what keeps me optimistic and engaged. Jon and the others here - your services to adult mental health and well-being cannot be underestimated.

Lisp and the foundations of computing

Posted Feb 11, 2019 14:41 UTC (Mon) by dgm (subscriber, #49227) [Link] (1 responses)

> Science is about all the wild things that are possible given infinite time, knowledge and resources.
> ...
> Engineering is about the practical things that people actually do and use given real-world time, knowledge and resources.

Maybe this is an engineer's view of science, but it's certainly not a very scientific view of either. In my humble oppinion, Science is about which things are possible and which are not, and *why*. Engineering is about *how* to make those things (or something close enough) at a reasonable cost.

Lisp and the foundations of computing

Posted Feb 11, 2019 21:26 UTC (Mon) by nix (subscriber, #2304) [Link]

Quite. Science can also sometimes tell you that the answer is "never, or at least not unless you fundamentally rethink it". That knowledge of limitations *alone* justifies all this maundering, even if nothing else useful came out of it. :)

Lisp and the foundations of computing

Posted Feb 11, 2019 21:24 UTC (Mon) by nix (subscriber, #2304) [Link]

hence no one ever tried to actually implement in C anything equivalent to Lisp's "eval"
That's not actually true. I know of at least three attempts, or things that could be called something like attempts (heck, one was even in glibc!).

(Yes, it was a mad idea in every case.)

Lisp and the foundations of computing

Posted Feb 16, 2019 18:53 UTC (Sat) by Wol (subscriber, #4433) [Link] (1 responses)

> > Well generate X and do Y then. These are all things you can do in C, or in any non-cripppled language. [...] C is not magic nor is it impossible to implement 'eval' in. It "just" depends on how precisely you define it.

> Science is about all the wild things that are possible given infinite time, knowledge and resources. From a very theoretical perspective C and Lisp are probably the same.

What a profound, spot on statement!

As soon as you start studying turing complete languages, the maths says they are equivalent, which means that they are in fact the same thing.

Let's go back to basic theory - about "what is a computer". A computer is an ALU, where you feed a (big?) number into the input slot, and a (big?) number comes out the output slot. What's to stop you feeding the output back in to the input slot? In other words - "there is no difference between program and data". That's basic computer theory - maths.

(Think about GCC, which is written in C, or any other "self-hosted" language. The code is - must be - both program and data otherwise it couldn't self-host.)

As soon as you start arguing about particular languages, like C++ or Lisp or Erlang or whatever, you are now into Technology or Engineering, where you can enforce arbitrary, random restrictions. Or maybe not, if you have a general-purpose turing-complete environment like a general-purpose computer. In other words, any turing-complete language is capable of having a execution environment written in it, capable of executing any random language. Turtles all the way down ...

Cheers,
Wol

Lisp and the foundations of computing

Posted Feb 18, 2019 23:12 UTC (Mon) by nix (subscriber, #2304) [Link]

As soon as you start studying turing complete languages, the maths says they are equivalent, which means that they are in fact the same thing.
Up to a point, Lord Copper. They are equivalent given an infinite tape and infinite time, and ignoring everything other than pure computation. I/O, random number generators, the time of day, anything at all to do with space or time complexity -- suddenly you are out of the Turing tarpit and languages and Turing-complete systems are distinguishable once more. i.e. just because you can emulate any Turing-complete system in any other doesn't mean you can emulate it at the same speed or in the same space.

So our systems are strictly more powerful than Turing machines -- but since infinite storage and time are not remotely feasible, we have also got no access to anything with the computational power of a "real" Turing machine, nor ever will. (And, of course, there is an unboundedly infinite hierarchy of Turing oracles, each of which can solve the halting problem in lower orders of oracle and in Turing machines themselves. We have even less idea how any of these might work: probably the answer is "they are defined by assertion and are not just unphysical but logically impossible entities".)

Lisp and the foundations of computing

Posted Feb 10, 2019 19:40 UTC (Sun) by rweikusat2 (subscriber, #117920) [Link]

There's an important difference between "Lisp eval" and "shell eval": The Lisp eval evaluates a form. Translating an input string to "a form" is done by the reader. Arbitray code generation in non-Lisp languages usually involves generating some form of "textual source code" which is then fed to an interpreter. This is fraught with peril because it often involves interpolating possibly untrusted input into a code template while trying to prevent whoever supplies the input from being able to effect arbitrary operations. Code generation in Lisp ultimatively means generating lists of Lisp objects.

Eg, this shell function

add() { eval "expr $1 + $2"; }
is prone to trivial code injection. A similar Lisp macro,
(defmacro add (a b) (if (and (atom a) (atom b)) `(+ ,a ,b) (error "Ha!")))
isn't.

Adding input validation for this contrived example to the shell code would obviously be fairly easy.

Lisp and the foundations of computing

Posted Feb 8, 2019 17:51 UTC (Fri) by krig (guest, #92101) [Link]

This comment is pretty much spot on in terms of nailing what I was trying to say in the talk.

It's not that data is code _in lisp specifically_ that is the point. It is the more general revelation that the boundary between data and code is a fluid one. This is something that bites people all the time, especially when dealing with user input. By using lisp, one gets practice in thinking about code and data as interchangeable, which helps spot potential issues when dealing with the code/data boundary in other languages.

Lisp and the foundations of computing

Posted Feb 10, 2019 5:50 UTC (Sun) by kmweber (guest, #114635) [Link]

Really glad the ENIAC Programmers Project is A Thing. One of the major historiographical themes in academic history of early electronic computing is the devaluation and marginalization (socially and economically) if not outright invisibility of women who were so central to the operation of these machines (see, for example, Janet Abbate, *Recoding Gender: Women's Changing Participation in Computing*; Nathan Ensmenger, *The Computer Boys Take Over: Computers, Programmers, and the Politics of Technical Expertise; and Marie Hicks, *Programmed Inequality: How Britain Discarded Women Technologists and Lost Its Edge in Computing*). But academic history, unfortunately, only reaches a limited audience (hopefully I can expand that audience a bit here by mentioning the monographs I did), and works like the ENIAC Programmers Project and even the recent(ish) film *Hidden Figures* might help to bring that corrective to a wider audience.


Copyright © 2019, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds