Search code examples
lambdacommon-lispread-eval-print-loopmarkerslisp-macros

Are lambda expressions macros or markers in Common Lisp?


I am trying to learn Common Lisp with the book Common Lisp: A gentle introduction to Symbolic Computation. In addition, I am using SBCL, Emacs and Slime.

In chapter 7, the author suggests the following about lambda expressions: enter image description here

This confuses me because the SBCL's REPL returns:

CL-USER> #'lambda
#<CLOSURE (:MACRO LAMBDA) {1000D736DB}>

Apparently, the author used Lisp Works (not 100% sure). I think this is not relevant to the difference described above. However, I thought it would be better to mention it anyway.

My SBCL's REPL also returns macro for the well-known macros such as and:

CL-USER> #'and
#<CLOSURE (:MACRO AND) {1000D7365B}>

Note that the behavior with "ordinary" functions such as append is different:

CL-USER> #'append
#<FUNCTION APPEND>

This post here seems to slightly touch the non-single nature of lambda expressions. However, it does not mention anything about markers.

Did I miss something about the nature of lambda expressions?


Solution

  • A way of thinking about this is to realise that the only way to get at a lexically-apparent function value in CL is the special operator function: if you want to get at the function associated with foo in the current lexical environment you have to say (function foo):

    (flet ((foo (x)
             x))
      (function foo)
    

    For instance. So function is the special operator which lets you see what is in the function namespace. function has some syntactic sugar, which is #', in the same way quote does (I'm not going to use this below). You can think of function application (f x y) as approximately like (<funcall> (function f) x y), where <funcall> is some magic thing which does not in turn get replaced like that.

    But you also want anonymous functions, and, well, you use function for that as well, with its argument being a lambda expression:

    lambda expression n. a list which can be used in place of a function name in certain contexts to denote a function by directly describing its behavior rather than indirectly by referring to the name of an established function; its name derives from the fact that its first element is the symbol lambda. – CLHS

    So an anonymous function is denoted as (function (lambda (...) ...)). You could, if you like, think of (lambda (...) ...) as the 'name' of a function. (People I think don't like this because it interferes with the idea of anonymous functions, but it's pretty clear that there is a countable set of possible function 'names' of the form (lambda (...) ...) and you could even enumerate this set I think (doing so would be fiddly in the same way that enumerating the rationals is fiddly).

    Since the function position in a function application is already interpreted in the namespace of functions, ((lambda (...) ...) ...) denotes an anonymous function application: it's more-or-less the same as (funcall (function (lambda (...) ...) ...) (see CLHS again).

    And this is how CL was at some point in the 1980s.

    There is then some historical confusion which I am unsure of: I remember it happening but I don't remember the ordering. First of all, in a Lisp-1 you don't need any of this function stuff: the form car denotes the function which gets the car of a cons, you don't need to say (function car). And similarly (lambda (...) ...) denotes a function. As well as this there was/is another proposed Lisp standard, which was/is ISLisp, which may be here. And although ISLisp isn't a Lisp-1, it does have a form (lambda (...) ...) which denotes a function.

    People wanted CL to be able to be compatible with ISLisp, which means that (lambda (...) ...) should denote a function. What everyone actually did was to secretly add a definition like:

    (defmacro lambda (args &body forms)
      `(function (lambda ,args ,@forms)))
    

    But, critically, you can't portably do that in CL, because lambda is cl:lambda and you're not allowed to redefine things in the CL package. People did it anyway but the result was that their programs were not portable, and often had to be decorated with special magic to unlock & relock the CL package.

    Well, the solution to that was that the language has to provide such a macro. And sometime between the language defined by CLtL1 and the final standard this happened, so now lambda has a macro definition whose expansion is (more-or-less: implementations may be allowed to do special things) the obvious expansion:

    (lambda (x) x)
     -> #'(lambda (x) x)
    

    So in modern CL:

    • A list beginning lambda denotes ('names') a function in a form like (function (lambda (...) ...) and also in the function position of a form, so ((lambda (x) x) 1), say.
    • lambda is defined as a macro which expands to (function (lambda (...) ...)) which makes it easier to use.

    Notes: the implementation you are using is, I think, allowed to do what it does for macros, but you should not rely on this:

    It is an error to use function on a function name that does not denote a function in the lexical environment in which the function form appears. Specifically, it is an error to use function on a symbol that denotes a macro or special form. An implementation may choose not to signal this error for performance reasons, but implementations are forbidden from defining the failure to signal an error as a useful behavior. – CLHS

    You should really use macro-function which will tell you if something has a macro definition.