// Lisp has gone through many implementations in its 70+ year history since // its original presentation at Dartmouth's inaugural AI workshop that coined the term, // https://en.wikipedia.org/wiki/Dartmouth_workshop // If you are mathematically inclined, you will note the names of Claude Shannon, John Nash, // and Ray Solomonoff, among others---and so many notable computer scientists. // We are going to work with GNU Common Lisp (GCL), CMU's Stone Bank Common Lisp (SBCL), // and Emacs' eLisp as the dialects of ANSI Common Lisp, and the Scheme simplifications // of Common Lisp that have been the vehicle of programming languages and advanced // compilation research. // The best books on Common Lisp are Paul Graham's free "On Lisp" // https://www.paulgraham.com/onlisp.html // and "ANSI Common Lisp" (available from multiple websites and on paper) // with Chapters 1 and 2 available from the author, https://paulgraham.com/acl.html // We discussed some of the historical conventions of Common Lisp (CL). // 1. CL's global names, a.k.a. symbols, are kept in a global hash table. Each // symbol that enters the hash table has multiple values associated with (unlike // Scheme, where each symbol has exactly one value, for simplicity). In CL, // these values include: SYMBOL-VALUE, SYMBOL-FUNCTION, and SYMBOL-PLIST (property list). // The symbols are said to be _bound_ to these values. // 2. The glolab hash table contains the entire world of a Lisp system. Replacing bindings // of global symbols allows the Lisp programmer to modify the system quickly and // easily, while it's running. The Lisp programmer gets Linux's eBPF functionality // almost for free (recall Alan Perlis' foreword we discussed). user@snowball lisp % rlwrap gcl -n -q '"' GCL (GNU Common Lisp) 2.6.14 Fri Jan 13 10:47:56 AM EST 2023 ANSI git: Version_2_6_14 [..skipped..] // One can make symbols out of stings: >(MAKE-SYMBOL "foo") > #:|foo| Top level. >(type-of (MAKE-SYMBOL "foo")) SYMBOL // This symbol is freshly made and has no bindings: Top level. >(boundp 'foo) NIL >(defun foo (x) (* x 2)) FOO // Now foo's SYMBOL-FUNCTION value is bound: >(fboundp 'foo) T // We can also separately set FOO's SYMBOL-VALUE: >(setq foo 100) 100 >(boundp 'foo) T // We now have both SYMBOL-FUNCTION and SYMBOL-VALUE for FOO, and // can now evaluate it in both the function position and argument position: >(foo foo) > 200 // We can check on these bindings: >(SYMBOL-VALUE 'foo) 100 >(SYMBOL-FUNCTION 'foo) (SYSTEM:LAMBDA-BLOCK FOO (X) (* X 2)) // SYMBOL-FUNCTION has a shorthand #' , similar to ' being a shorthand for QUOTE: >#'foo > (SYSTEM:LAMBDA-BLOCK FOO (X) (* X 2)) // Note the body of the function bound to foo. It's a first-class value, a function // that can be passed around as a argument to other functions >(defun apply-twice (f x) (funcall f (funcall f x))) APPLY-TWICE >(APPLY-TWICE #'foo 5) 20 // This body is called a LAMBDA. Implementations of LAMBDAs differ between Lisps, // but in GCL it's just a list: >(type-of #'foo) CONS Top level. >(funcall '(SYSTEM:LAMBDA-BLOCK FOO (X) (* X 2)) 100) 200 // We could bind FOO's SYMBOL-FUNCTION to a new lambda without a DEFUN. // In fact, DEFUN is just a convenience wrapper over the special form SETF, // which can set anything. Same as DEFUN: >(setf (SYMBOL-FUNCTION 'foo) (lambda (x) (* x 3))) (SYSTEM:LAMBDA-CLOSURE () () () (X) (* X 3)) >(foo foo) 300 // Same as (SETQ FOO 200): >(setf (SYMBOL-VALUE 'foo) 200) 200 >(foo foo) 600 ============ To be continued =========== // At this point we switched to Scheme for our in-class exercises. // Scheme is more compact, has only _one_ value for a symbol, // uses (DEFINE (foo x) (...)) instead of (DEFUN foo (x) (..) to // define functions, (DEFINE X) to introduce a symbol, and // SET! instead of SETQ. // // True and False values are separate, #T and #F, the empty list is '(). // There is no NIL that doubles as false and empty list, which removes // a potential type confusion, but confuses Lisp-ers like myself. user@snowball lisp % rlwrap guile GNU Guile 3.0.10 Copyright (C) 1995-2024 Free Software Foundation, Inc. Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. scheme@(guile-user)> (define (inc x) (+ x 1)) scheme@(guile-user)> (inc 5) $1 = 6 // Oops, no NIL: scheme@(guile-user)> (define (map f l) (if (nil? l) nil (cons (f (car l)) (map f (cdr l))))) ;;; :3:31: warning: possibly unbound variable `nil' scheme@(guile-user)> (define (map f l) (if (nil? l) '() (cons (f (car l)) (map f (cdr l))))) scheme@(guile-user)> (map inc '(1 2 3 4)) $2 = (2 3 4 5) scheme@(guile-user)> (define (countdown n) (if (= n 0) (cons 0 '()) (cons n (countdown (- n 1))))) scheme@(guile-user)> (countdown 5) $3 = (5 4 3 2 1 0) // IF takes #T or #F: scheme@(guile-user)> (if #t 1 0) $4 = 1 // Symbols need to be DEFINE-d before being bound with SET! : scheme@(guile-user)> (set! x #t) ;;; :22:0: warning: possibly unbound variable `x' ice-9/boot-9.scm:1676:22: In procedure raise-exception: Unbound variable: x Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. scheme@(guile-user) [1]> ,q scheme@(guile-user)> (define x) scheme@(guile-user)> (set! x #t) scheme@(guile-user)> (if x 1 0) $5 = 1 scheme@(guile-user)> (if (not x) 1 0) $6 = 0 scheme@(guile-user)> (if #t 1 0) $7 = 1 // This is an error, because #t is in the function call position: scheme@(guile-user)> (if (#t) 1 0) ice-9/boot-9.scm:1676:22: In procedure raise-exception: Wrong type to apply: #t Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. scheme@(guile-user) [1]> ,q // A recursive append, just a little counter-intuitive: scheme@(guile-user)> (define (append x l) (if (nil? l) (cons x '()) (cons (car l) (append x (cdr l))))) scheme@(guile-user)> (append 1 '()) $8 = (1) scheme@(guile-user)> (append 1 '(3 2)) $9 = (3 2 1)