// Here is a transcript of my GCL session in class, errors and all. // Lines starting with two slashes are my comments; the // rest are GCL's input and output. // The LISP interpreter is GCL, GNU Common Lisp. I installed it // via MacPorts with "port install gcl"; it's "apt-get install gcl" on Debian Linux. // // GCL supports tab-completion of standard symbol names, the help function for online help, // and nice command-line editing (via the classic Unix Readline library // that lets you scroll through history, search it for prior commands // with Ctrl+r and so on). I like it for these reasons, but there // are other nice alternatives available through MacPorts: e.g., CLISP // ("port install clisp"). There are other implementations available, // too, like SBCL---you will end up using them if you choose a LISP-based // project where a major library requires a specific feature or flavor. // Remember, ' in front of an expression, the same as (quote ), // means, "don't evaluate me, pass me to the function as you built me // when you parsed me, in the 'Read' part of the Read-Eval-Print Loop (REPL)". $ gcl GCL (GNU Common Lisp) 2.6.12 ANSI Nov 27 2014 05:23:43 Source License: LGPL(gcl,gmp), GPL(unexec,bfd,xgcl) Binary License: GPL due to GPL'ed components: (READLINE UNEXEC) Modifications of this banner must retain notice of a compatible license Dedicated to the memory of W. Schelter Use (help) to get some basic information on how to use GCL. Temporary directory for compiler files set to /private/var/folders/1h/3prjrhy96y55j8jrvttgd_kw0000gp/T/ // Pressing TAB shows possible completions. This is very useful when you don't // remember function names yet. // In GCL, symbol names are automatically converted to uppercase. // Here are functions and/or special forms that start with SYMBOL. We'll come back // to these later. >(SYMBOL SYMBOL SYMBOL-NAME SYMBOL-PLIST SYMBOLP SYMBOL-FUNCTION SYMBOL-PACKAGE SYMBOL-VALUE >(CA CAAAAR CAADR CADDAR CAR CAAADR CAAR CADDDR CASE CAAAR CADAAR CADDR CATCH CAADAR CADADR CADR CAADDR CADAR CALL-ARGUMENTS-LIMIT // Functions evaluate their arguments recursively. So what CAR gets // is the number 3, not a cons cell. This results in error: >(car (+ 1 2)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by CAR. Condition in CAR [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 3 is not of type LIST: // GCL's internal debugger is very powerful, but I rarely find myself using it. // So here I quit out of it, to the top-level prompt & environment. Broken at CAR. Type :H for Help. 1 Return to top level. >>1 Top level. // Let's make a list. A list consists of cons cells. This // is the most basic way of making a list: // Oops, I forgot the second argument for the last cons. Wrong arity gets you an error: >(cons 1 (cons 2 (cons 3))) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by CONS. Condition in CONS [or a callee]: INTERNAL-SIMPLE-PROGRAM-ERROR: CONS [or a callee] requires more than one argument. Broken at CONS. Type :H for Help. 1 Return to top level. >>1 Top level. // This works: >(cons 1 (cons 2 (cons 3 nil))) (1 2 3) // A much easier way, which is equivalent to the above, and makes the same structures in memory: >(list 1 2 3) (1 2 3) >(CAR (list 1 2 3)) 1 >(car (cons 1 2)) 1 // Note, (cons 1 2) is a dotted pair, (1 . 2) >(cdr (list 1 2 3)) (2 3) >(cdr (cons 1 2)) 2 // Note: CAR is a function, and its argument is evaluated. Thus // CAR here gets 3 as argument, which raises an error: >(CAR (+ 1 2)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by CAR. Condition in CAR [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 3 is not of type LIST: Broken at CAR. Type :H for Help. 1 Return to top level. >>1 Top level. // Tried this on a whim. The result looks weird. What's going on? >(car (list + 1 2)) (CAR (+ 1 2)) // Well, list is a function. Its arguments are all evaluated; and + happens // to have a special value when used as an argument (rather than a function name). // What we get is the value of that variable at that moment. // + is the last expression as read by the GCL interpreter. So it's value was (CAR (+ 1 2)), // the previous expression as read, (CAR (+ 1 2)) // Let's try this again: >(car (list 10 1 2)) 10 // + now changes value to the last expression as read: >+ (CAR (LIST 10 1 2)) // * is also a special variable: the _result_ of evaluating the last expression >* (CAR (LIST 10 1 2)) // Inserted example of + and * : >(+ 1 2) 3 >+ (+ 1 2) >(+ 1 2) 3 >* 3 // Back to in-class transcript: >(* 2 3) 6 // Evaluating every argument recursively may be tedious or not what we // want at all (think of IF with THEN and ELSE expressions: we only // ever want to evaluate one of them, not both! // So LISP has *special forms* that look like functions but whose // arguments (some or all) are not evaluated. The most basic of // these is QUOTE. Evaluating QUOTE gives its argument verbatim: >(quote 2) 2 >(quote (+ 1 2)) (+ 1 2) >(quote (list 1 2 3)) (LIST 1 2 3) // The CAR of this list is the symbol LIST: >(car (quote (list 1 2 3))) LIST // Symbol LIST has a function-value (the compiled function that creates cons cells), // but is not bound to any value: >list Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on LIST: Unbound variable: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // Note: HELP is a function, so its argument needs to be quoted! // 'X is shorthand for (quote X). 'quote is thus (quote quote) >(HELP 'quote) ----------------------------------------------------------------------------- QUOTE [Special form] // <=== special form! (not found "gcl.info") From ((QUOTE . Special Forms and Functions) gcl-si.info): -- Special Form: QUOTE Package:LISP Syntax: (quote x) or 'x Simply returns X without evaluating it. ----------------------------------------------------------------------------- // This expression has once cons cell and one atom: >(list 1 ) (1) >(list 1 2 3) (1 2 3) // List created without LIST, with shorthand QUOTE: >(car '(1 2 3)) 1 // Remember, car is a function that evaluates its arguments. What would // happen if we forgot ' in the above expression? Try it. >(car (quote (1 2 3))) 1 // If is another special form: >(if (< 2 3) 10 100) 10 >(if (< 3 3) 10 100) 100 // BOUNDP is the predicate that checks if a symbol has a value bound to it: >(boundp '*) T >(boundp '+) T >(boundp '<) NIL // FBOUNDP checks if a symbol has a function bound to it. Remember, it can have both // bindings, either, or none. The Scheme dialect of LISP did away with that, // leaving only one binding for a symbol. >(fboundp '<) T >(fboundp '*) T >(help '*) ----------------------------------------------------------------------------- * [Function] <== both a function... ----------------------------------------------------------------------------- * [Special variable] <== and a special variable (not found "gcl.info") From ((* . Numbers) gcl-si.info): -- Function: * (&rest numbers) Package:LISP Returns the product of its arguments. With no args, returns 1. ----------------------------------------------------------------------------- >(*) 1 >(help '+) ----------------------------------------------------------------------------- + [Function] ----------------------------------------------------------------------------- + [Special variable] (not found "gcl.info") From ((+ . Numbers) gcl-si.info): -- Function: + (&rest numbers) Package:LISP Returns the sum of its arguments. With no args, returns 0. ----------------------------------------------------------------------------- >(+) 0 >(help '<) ----------------------------------------------------------------------------- < [Function] (not found "gcl.info") From ((< . Numbers) gcl-si.info): -- Function: < (number &rest more-numbers) Package:LISP Returns T if its arguments are in strictly increasing order; NIL otherwise. ----------------------------------------------------------------------------- >(ATOM Display all 956 possibilities? (y or n) >(A ABORT APROPOS ARRAY-RANK-LIMIT ABS APROPOS-LIST ARRAY-ROW-MAJOR-INDEX ACONS AREF ARRAY-TOTAL-SIZE ACOS ARITHMETIC-ERROR ARRAY-TOTAL-SIZE-LIMIT ACOSH ARITHMETIC-ERROR-OPERANDS ARRAYP ADJOIN ARITHMETIC-ERROR-OPERATION ASH ADJUST-ARRAY ARRAY ASIN ADJUSTABLE-ARRAY-P ARRAY-DIMENSION ASINH ALLOCATE ARRAY-DIMENSION-LIMIT ASSERT ALPHA-CHAR-P ARRAY-DIMENSIONS ASSOC ALPHANUMERICP ARRAY-DISPLACEMENT ASSOC-IF AND ARRAY-ELEMENT-TYPE ASSOC-IF-NOT APPEND ARRAY-HAS-FILL-POINTER-P ATAN APPLY ARRAY-IN-BOUNDS-P ATANH APPLYHOOK ARRAY-RANK ATOM // ATOM is a predicate that tests whether an argument is an atom. Wish it were // called ATOMP, with the ending "P" for predicate. Scheme ends all of its // predicate names with "?" >(ATOM nil) T // One does not have a bound function value! >(1 . nil) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 1 is not of type FUNCTION: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // Making a single cons cell >(cons 1 nil) (1) // Making a dotted pair >(cons 1 2) (1 . 2) // LIST is a function, argument evaluation is recursive... >(list 1 ( 1 . 2)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 1 is not of type FUNCTION: Broken at LIST. Type :H for Help. 1 Return to top level. >>1 Top level. // This is how one creates a dotted pair in a list: >(list 1 (cons 1 2)) (1 (1 . 2)) >(list 1 (cons 1 2) 3) (1 (1 . 2) 3) // Let's write a sum function for a list. But first, let's see if one exists already, // either as a function or a symbol with bound value: >(sum 1 2) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on SUM: Undefined function: Broken at EVAL. Type :H for Help. 1 Return to top level. >>sum Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on SUM: Unbound variable: Broken at EVAL. 1 (abort) Return to debug level 1. 2 Return to top level. >>>2 Top level. >sum Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on SUM: Unbound variable: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // An aside: let's pay attention to error messages when a symbol's not bound: >x Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on X: Unbound variable: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // ... and when it has no function value: >(x 1) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on X: Undefined function: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // We can set a symbol's value: >(setq x 100) 100 >x 100 // ...but its function value is still not defined: >(x 1) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on X: Undefined function: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // Well, let's define it: >(defun x (y) (+ y 1)) X // Now X has both a bound value and a bound function value, and can be used in // both the first "function" position and argument positions: >x 100 >(x x) 101 // This won't work, though, because X's arity is checked: >(x x x) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by X. Condition in X [or a callee]: INTERNAL-SIMPLE-PROGRAM-ERROR: X [or a callee] requires less than two arguments. Broken at X. Type :H for Help. 1 Return to top level. >>1 Top level. // This also works recursively, of course: >(x (x x)) 102 // Some more predicates: >(atom 1) T >(atom nil) T >(atom "oo") T >(atom (cons 1 2)) NIL >(atom 1 2) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by ATOM. Condition in ATOM [or a callee]: INTERNAL-SIMPLE-PROGRAM-ERROR: ATOM [or a callee] requires less than two arguments. Broken at ATOM. Type :H for Help. 1 Return to top level. >>1 Top level. // What caused this error? >(BOUNDP x) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by BOUNDP. Condition in BOUNDP [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 100 is not of type SYMBOL: Broken at BOUNDP. Type :H for Help. 1 Return to top level. >>1 Top level. // Well, boundp is a function, its arguments are evaluated: >(help 'boundp) ----------------------------------------------------------------------------- BOUNDP [Function] (not found "gcl.info") From ((BOUNDP . Symbols) gcl-si.info): -- Function: BOUNDP (symbol) Package:LISP Returns T if the global variable named by SYMBOL has a value; NIL otherwise. ----------------------------------------------------------------------------- >(BOUNDP 'x) T >(BOUNDP 'y) NIL // Any symbols _read in_ already exist as "internal" (interned) in the global hash // table, even through unbound. >(FIND-SYMBOL "Y") Y :INTERNAL // whereas for symbols never typed in: >(FIND-SYMBOL "FOO") NIL NIL >foo Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on FOO: Unbound variable: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // but once read, they stick around, unbound: >(FIND-SYMBOL "FOO") FOO :INTERNAL >(BOUNDP 'foo) NIL >(help 'list) ----------------------------------------------------------------------------- LIST [Function] (not found "gcl.info") From ((LIST . Lists) gcl-si.info): -- Function: LIST (&rest args) Package:LISP Returns a list of its arguments ----------------------------------------------------------------------------- >(setq foo "bar") "bar" >(BOUNDP 'foo) T >(FIND-SYMBOL "FOO") FOO :INTERNAL >(FBOUNDP 'x) T >(FBOUNDP 'foo) NIL >foo "bar" >(foo 1) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on FOO: Undefined function: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // FBOUNDP checks if a function binding exists, SYMBOL-FUNCTION would get it if it does, or error out: >(SYMBOL-FUNCTION 'foo) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by SYMBOL-FUNCTION. Condition in SYMBOL-FUNCTION [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on FOO: Undefined function: Broken at SYMBOL-FUNCTION. Type :H for Help. 1 Return to top level. >>1 Top level. >(SYMBOL-FUNCTION 'x) (LAMBDA-BLOCK X (Y) (+ Y 1)) // Testing if the argument is a cons cell: >(CON CONCATENATE CONDITION CONSP CONTINUE CONCATENATED-STREAM CONJUGATE CONSTANTLY CONTROL-ERROR COND CONS CONSTANTP >(CONS CONS CONSP CONSTANTLY CONSTANTP >(CONS CONS CONSP CONSTANTLY CONSTANTP >(CONSP 1) NIL >(CONSP '(1 2)) T // ..also a function, args are evaluated, as usual: >(CONSP (1 2)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 1 is not of type FUNCTION: Broken at CONSP. Type :H for Help. 1 Return to top level. >>1 Top level. >(CONSP (x 2)) NIL >x 100 >(CONSP '(x 2)) T >(CONSP '(z 2)) T >(CONSP (quote (z 2))) T // Z is now a symbol, created by read (and unbound) >(FIND-SYMBOL "Z") Z :INTERNAL >(BOUNDP 'z) NIL // Oops, I meant NULL >(NIL nil) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on NIL: Undefined function: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. // NULL is the predicate that checks if the argument is a NIL >(NULL nil) T >(NULL 1) NIL >(NULL (cons 1 nil)) NIL // Let's define a function that adds all numbers in a list. // We have IF and tests, but no loops. But we can do this recursively. // Sum of a list is whatever number comes first, plus the sum of the rest, right? // First try, full of bugs: >(defun sum (l) (+ (car l) (sum l)) ) SUM // and now it's bound as a function: >(FBOUNDP 'SUM) T >(sum '(1 2 3)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by SUM. Condition in SUM [or a callee]: INTERNAL-SIMPLE-ERROR: Invocation history stack overflow. Broken at +. Type :H for Help. 1 Return to top level. >>1 // We need a test to stop infinite recursion. The sum of an empty list is 0: Top level. >(defun sum (l) (if (consp l) (+ (car l) (sum l)) 0)) SUM // Oops, where's the bug now? >(sum '(1 2 3)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by CONSP. Condition in CONSP [or a callee]: INTERNAL-SIMPLE-ERROR: Invocation history stack overflow. Broken at CONSP. Type :H for Help. 1 Return to top level. >>1 Top level. // Ah, passing the same list, not the rest of it to the recursive call to sum. // Fixing it: >(defun sum (l) (if (consp l) (+ (car l) (sum (cdr l))) 0)) SUM // And now it works. Behold: summing up a list with no explicit looping at all: >(sum '(1 2 3)) 6 // Things to note: // 1. Recursion always needs a test for when to stop, and a value to return in that basic case // 2. There are no "statements" in this function; it consists of expressions that all have values. // For example, IF has a value (either that of its THEN or ELSE argument) // 3. When working with lists as arguments, CAR and CDR are necessary to "deconstruct" the cons cell, // i.e., get the value and the "rest" out of it. Nothing can be done with a cons cell as such. //