Why does the function composition of two "not" functions in CL result in nil according to this book?

Viewed 98

I am studying the book Common LISP: A Gentle Introduction to Symbolic Computation. The book is around 40 yrs old and, apparently, somewhat a classic.

In chapter 1, the author uses boxes as a way of explaining functions with inputs and outputs. This is one of the drawings: enter image description here

Actually, the drawing above is the answer of this question. I got the first and the second question wright: enter image description here

However, the third question seems weird: enter image description here

The composition of (not (not ...)) depends on the "..." variable part. For instance, the terminal shows:

CL-USER> (not (not 12))
T
CL-USER> (not (not nil))
NIL

This book exercise seems wrong in some way. But, since I do not know much about CL I thought it would be better to ask if more experienced developer in CL also think this is a strange point in the answer sheet.

I am using SBCL, Slime and Common Lisp. I am new to CL but I have some experience in Racket.

Thanks in advance

3 Answers

Exercise 1.14:

Actually the author wants us to use the symbol NOT as an input to the function NOT. The input is not the function named NOT and also not the variable named NOT. Thus the symbol needs to be quoted in a computation:

> (NOT 'NOT)
NIL

Since every symbol is true, not of true is false. false in Common Lisp is the symbol NIL.

What is different between the symbols NIL and NOT ?

  • NIL evaluates to itself
  • NOT has no default value and evaluates as a variable. To evaluate the symbol NOT to itself, it needs to be quoted.

This is explained in chapter 3.8 USING SYMBOLS AND LISTS AS DATA in the old edition.

The exercise is not showing composition of functions; it is showing the application of the function not to the argument not. In Common Lisp all values are true except for nil (and 'nil, (), and '()), so applying not to not must evaluate to nil.

There is a caveat: not is a function in Common Lisp, but there are multiple name spaces in CL. If you want to try this in the REPL, you will have to specify that you mean the function not, not the variable not by using either function or #' (sharp-quote):

CL-USER> (not (function not))
NIL
CL-USER> (not #'not)
NIL
CL-USER> (not not)  ; --> The variable NOT is unbound.

Hmmm. Some of us were too lazy to actually look up the exercise in the book; it appears that at this point in the book only numbers and symbols have been discussed (duh!). The author's intention was that not be a symbol, which means that it must be quoted in a function call.

Since not is a function, it evaluates its arguments. The call (not not) evaluates the argument not, but not is an unbound variable so an error is issued. To pass the symbol not to the function not, the argument must be quoted either with quote (which is not a function, but a special operator: quote does not evaluate its arguments) or a quote mark ('). The quoted not evaluates to a symbol:

CL-USER> not  ;  -->  The variable NOT is unbound.
CL-USER> (quote not)
NOT
CL-USER> 'not
NOT
CL-USER> (symbolp 'not)
T
CL-USER> (not 'not)
NIL

Since nil is the only symbol that is false in a boolean context, the symbol not must be true, so (not 'not) must be false.

It's not a composition. It is a function call

(not not)

The function not receives a value, which is the function not, as its argument. Since this value is not NIL, the return value is NIL:

not:
--------------------
NIL      --->   T           (not NIL)
non-NIL  --->   NIL         (not T), (not 1), (not not), (not (list 1)), ...
--------------------

Any non-NIL value is truthy, so not returns a NIL for it.

Of course (not not) works in Scheme which is Lisp-1. In Common Lisp which is Lisp-2, the call is (not #'not).

update: the call is actually (not 'not), just as the preceding example's code in the book is (not 'fred).

Related