84

On this site they say there are 10 LISP primitives. The primitives are: atom, quote, eq, car, cdr, cons, cond, lambda, label, apply.

http://hyperpolyglot.wikidot.com/lisp#ten-primitives

Stevey reckons there are seven (or five):

Its part of the purity of the idea of LISP: you only need the seven (or is it five?) primitives to build the full machine. http://steve-yegge.blogspot.com/2006/04/lisp-is-not-acceptable-lisp.html

What is the minimum number of primitives to build a LISP machine (ie something that can run an eval/value function on LISP code)? (And which ones are they?)

(I can understand you could live without atom, label and apply)

6 Answers 6

62

Basic Predicates/F-functions

McCarthy's Elementary S-functions and Predicates were:

  1. atom

    Which was necessary because car and cdr are defined for lists only, which means you cannot count on any sort of answer to indicate what was happening if you gave car an atom.

  2. eq

    For testing equality between atoms.

  3. car

    For returning the first half (address) of the cons cell. (Contents of address register).

  4. cdr

    For returning the second half (decrement) of the cons cell. (Contents of decrement register).

  5. cons

    For making a new cons cell, with the address half containing the first argument to cons, and the decrement half containing the second argument.

Tying it together: S-Functions

He then went on to add to his basic notation, to enable writing what he called S-functions:

  1. quote

    To represent an expression without evaluating it.

  2. cond

    The basic conditional to be used with the previously described predicates.

  3. lambda

    To denote a function.

  4. label

    Though he didn't need this for recursion, he might not have known about the Y-Combinator (according to Paul Graham), he added this for convenience and to enable easy recursion.


So you can see he actually defined 9 basic "operators" for his Lisp machine. In a previous answer to another one of your questions, I explained how you could represent and operate on numbers with this system.

But the answer to this question really depends on what you want out of your Lisp machine. You could implement one without the label function, as you could simply functionally compose everything, and obtain recursion through applying the Y-Combinator.

atom could be discarded if you defined the car operation on atoms to return NIL.

You could essentially have McCarthy's LISP machine with 7 of these 9 defined primitives, but you could ostensibly define a more concise version depending on how much inconvenience you'd want to inflict on yourself. I like his machine quite fine, or the many primitives in the newer languages like Clojure.

4
  • 22
    The suggestion that McCarthy didn't know about the Y-Combinator appears to be in error. On page 7 of "Recursive Functions...", McCarthy writes: There is a notation involving operators that are called combinators for combining functions without the use of variables. Unfortunately, the combinatory expressions for interesting combination of functions tend to be lengthy and unreadable. Commented Aug 20, 2011 at 17:59
  • 1
    There is something missing here. Such a lisp couldn't add two numbers or even understand that 12 is a number. Commented Mar 20, 2018 at 10:45
  • 3
    It can indeed! I wrote a blog post on it, too. blog.isaachodes.io/p/set-theory-and-lisp
    – Isaac
    Commented Mar 21, 2018 at 16:27
  • 2
    To be sure, it wouldn't use the traditional machine representation of the integers, and would be rather inefficient as a result.
    – Isaac
    Commented Mar 21, 2018 at 16:28
19

The best way to actually know this for sure is if you implement it. I used 3 summers to create Zozotez which is a McCarty-ish LISP running on Brainfuck.

I tried to find out what I needed and on a forum you'll find a thread that says You only need lambda. Thus, you can make a whole LISP in lambda calculus I you'd like. I found it interesting, but it's hardly the way to go if you want something that eventually has side effects and works in the real world.

For a Turing complete LISP I used Paul Grahams explanation of McCarthy's paper and all you really need is:

  • symbol-evaluation
  • special form quote
  • special form if (or cond)
  • special form lambda (similar to quote)
  • function eq
  • function atom
  • function cons
  • function car
  • function cdr
  • function-dispatch (list-lambda)

Thats 10. In addition to this, to have a implementation that you can test and not just on a drawing board:

  • function read
  • function write

Thats 12. In my Zozotez I implemeted set and flambda (anonymous macroes, like lambda) as well. I could feed it a library implementing any dynamic bound lisp (Elisp, picoLisp) with the exception of file I/O (because the underlying BF does not support it other than stdin/stdout).

I recommend anyone to implement a LISP1-interpreter in both LISP and (not LISP) to fully understand how a language is implemented. LISP has a very simple syntax so it's a good starting point for a parser. I'm currently working on a scheme compiler written in scheme with different targets (like Stalin is for target C), hopefully BF as one of them.

4
  • 3
    Regarding the use of nothing but lambda, compare with "One instruction set computer", "NAND logic", "SKI combinator calculus", ... :-)
    – ajm475du
    Commented Sep 11, 2014 at 14:03
  • 2
    @ajm475du All of those are the same as "you only need lambda". It's turing complete but almost impossible to use because of lack of I/O. BF only need 6 instructions to be turing complete. the rest if to make it practical.
    – Sylwester
    Commented Sep 12, 2014 at 13:58
  • 1
    Hmm. What if you connect stdin/stdout of the bf interpreter to another program that can interpret file/io commands? Then bf-lisp could write requests and then read from the requested file. Commented Dec 3, 2014 at 18:32
  • 2
    @luserdroog What you are suggesting is using stdin/stdout as a message bus to some program/OS to implement system calls. I'm actually thinking of doing that for my compiler which will compile to BF. Eg. if you use more I/O than read/write the program sends a magic requirement-string and teh API would give handhake pretty much the same way as you got errors when running windows programs in DOS back in the 90s. Notice that BF still needs to provide terminal, thus I/O to begin with so it's just further expansion.
    – Sylwester
    Commented Dec 3, 2014 at 22:38
10

McCarthy used seven operators to define the original Lisp: quote, atom, eq, car, cdr, cons and cond. This article retraces his steps.

3
  • 1
    He actually also used label, though it wasn't necessary to have.
    – Isaac
    Commented Aug 14, 2010 at 16:11
  • 2
    And he needed lambda as well.
    – Isaac
    Commented Aug 14, 2010 at 16:20
  • 10
    I was confused about this at first too, but he actually defines lambda and label in terms of the seven primitives given. He just introduces what he intends them to mean before he gives their implementation in the definition of eval in section 4. You can see that the implementation of eval provides support for lambda/list without itself depending on either.
    – amalloy
    Commented May 19, 2011 at 3:24
9

This faq states:

There is no single "best" minimal set of primitives; it all depends on the implementation. For example, even something as basic as numbers need not be primitive, and can be represented as lists. One possible set of primitives might include CAR, CDR, and CONS for manipulation of S-expressions, READ and PRINT for the input/output of S-expressions and APPLY and EVAL for the guts of an interpreter. But then you might want to add LAMBDA for functions, EQ for equality, COND for conditionals, SET for assignment, and DEFUN for definitions. QUOTE might come in handy as well.

That comes from the School of Computer Science, Carnegie Melon website.

1
  • 1
    It comes from the Lisp FAQ, a copy of which is hosted by the Carnegie Mellon University School of Computer Science. (It’s important to be precise with attribution on academic topics, particularly when imputing authority.) Commented Oct 2, 2023 at 1:49
5

You just need an x86 MOV instruction.

"The M/o/Vfuscator (short 'o', sounds like "mobfuscator") compiles programs into "mov" instructions, and only "mov" instructions. Arithmetic, comparisons, jumps, function calls, and everything else a program needs are all performed through mov operations; there is no self-modifying code, no transport-triggered calculation, and no other form of non-mov cheating."

Seriously though, these primitives will not implement a Lisp Machine. A machine needs facilities like I/O, and garbage collection. Not to mention a function calling mechanism! Okay, you have seven primitives which are functions. How does the machine call a function?

The proper understanding of what these primitives make possible is that they expose the instruction set of a Universal Turing Machine. Because those instructions are "Lispy", by a slip of the tongue (speaking with a Lisp) we sneakily call this a "Lisp Machine". "Universal" means that the machine is programmable: with some combination instructions applied to the Universal Turing Machine, we can instantiate any Turing Machine. But so far, all of that is purely a mathematical construct.

To actually simulate this UTM—realize it physically in order to explore it on a computer, we need a machine which provides for a way to us to actually input those forms which create Turing Machines from combinations of those seven Lisp instructions. And we also need some form of output; the machine as to at least be able to tell us "yes", "no", or "Wait, I'm still running".

In other words, the only way those seven instructions can practically work is if they are hosted in a larger machine which provides the environment.

Also note that Graham's seven primitives have no explicit support for numbers, so you would have to build them out of functions ("Church numerals" technique). No production Lisp implementation does such a crazy thing.

1
  • 2
    Love it. I would ask a question about UTMs but I think you've already smashed it. I'm trying to think of a question involving home brew 8-but computing, UTMs and Lisp.
    – hawkeye
    Commented Aug 6, 2017 at 22:47
2

Paul Graham implements eval using seven.

In McCarthy's Micro Manual for LISP he implements eval using ten.

Not the answer you're looking for? Browse other questions tagged or ask your own question.