;;;;------------------------- Macros ------------------------- ;;;; SETQ is a special form that doesn't evaluate its first argument. ;;;; It serves to save typing compared to SET, which does, and so ;;;; needs its first argument to be quoted: >(setq x 1001) 1001 >(set 'x 1001) 1001 >(set x 1001) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by SET. Condition in SET [or a callee]: INTERNAL-SIMPLE-TYPE-ERROR: 1001 is not of type SYMBOL: Broken at SET. Type :H for Help. 1 Return to top level. >>1 Top level. >(help 'setq) ----------------------------------------------------------------------------- SETQ [Special form] (not found "gcl.info") From ((SETQ . Symbols) gcl-si.info): -- Special Form: SETQ Package:LISP Syntax: (setq {var form}*) VARs are not evaluated and must be symbols. Assigns the value of the first FORM to the first VAR, then assigns the value of the second FORM to the second VAR, and so on. Returns the last value assigned. ----------------------------------------------------------------------------- ;; Recall that IF is another special form (it does not evaluate its ;; then and else clauses until the test is evaluated, then it picks ;; one or the other to eval: >(help 'if) ----------------------------------------------------------------------------- IF [Special form] (not found "gcl.info") From ((IF . Special Forms and Functions) gcl-si.info): -- Special Form: IF Package:LISP Syntax: (if test then [else]) If TEST evaluates to non-NIL, then evaluates THEN and returns the result. If not, evaluates ELSE (which defaults to NIL) and returns the result. ----------------------------------------------------------------------------- ;; With DEFMACRO, you can make your own special forms. See OnLisp Ch. 7. ;; The body of DEFMACRO is the code that produces a sexp to evaluate ;; when the macro is called. ;; This works: >(defmacro mysetq (sym val) (list 'set (list 'quote sym) val)))) MYSETQ >(MACROEXPAND '(mysetq x (+ 2 5)) ) (SET 'X (+ 2 5)) T >x 1001 >(mysetq x 2000) 2000 >x 2000 ;; This would not work: >(defmacro mysetq (sym val) (list 'set '(quote sym) val)))) MYSETQ ;; ...observe how we get SYM not X in the resulting expression; this is not what we wanted! >(MACROEXPAND '(mysetq x (+ 2 5))) (SET 'SYM (+ 2 5)) T >(mysetq x 2000) 2000 >(mysetq x 3000) 3000 >x 2000 >sym 3000 ;; Writing multiple LISTs is cumbersome. Instead, this special syntax makes ;; programming macros natural: >`(a b c) (A B C) ;; same as: >(list 'a 'b 'c) (A B C) ;; To get a value into the result, we need: >(list 'a 'b 'c x) (A B C 2000) ;; ...but with ` X gets quoted too: >`(a b c x) (A B C X) ;; So the shorthand for "actually evaluate me"=="strip the quote I'd otherwise get under `" ;; is this: >`(a b c ,x) (A B C 2000) ;; So this works too: >(defmacro mysetq (sym val) `(set (quote ,sym) ,val))) MYSETQ >(MACROEXPAND '(mysetq x (+ 3 10)) ) (SET 'X (+ 3 10)) T >(mysetq x (+ 3 10)) 13 >x 13 ;; DEFUN is also a macro! ;; Note that it first creates the symbol for the function name, ADD1, then ;; side-effects its function value with SETF, a very special form that, ;; instead of just evaluating its first argument, _resets the value of the cell ;; that would contain it_. So READ of the REPL creates the symbol ADD1, but ;; its SYMBOL-FUNCTION and its SYMBOL-VALUE are both nil. However, ;; instead of returning the error of "Undefined function", SETF instead ;; reset the function value of ADD1 to the provided lambda. ;; >(MACROEXPAND '(defun add1 (x) (+ x 1))) (PROGN (SETF (SYMBOL-FUNCTION 'ADD1) #'(LAMBDA (X) (BLOCK ADD1 (+ X 1)))) 'ADD1) T ;; SETF, in fact, modifies many behaviors of accessors. It's the Swiss army knife ;; of destructively modifying objects. ;; Note that SETF is itself a macro! >(help 'setf) ----------------------------------------------------------------------------- SETF [Special form and Macro] (not found "gcl.info") From ((SETF . Special Forms and Functions) gcl-si.info): -- Special Form: SETF Package:LISP Syntax: (setf {place newvalue}*) Replaces the value in PLACE with the value of NEWVALUE, from left to right. Returns the value of the last NEWVALUE. Each PLACE may be any one of the following: A symbol that names a variable. A function call form whose first element is the name of the following functions: nth elt subseq rest first ... tenth c?r c??r c???r c????r aref svref char schar bit sbit fill-poiter get getf documentation symbol-value symbol-function symbol-plist macro-function gethash char-bit ldb mask-field apply where '?' stands for either 'a' or 'd'. the form (THE type place) with PLACE being a place recognized by SETF. a macro call which expands to a place recognized by SETF. any form for which a DEFSETF or DEFINE-SETF-METHOD declaration has been made. ----------------------------------------------------------------------------- ;; You could implement RPLACA with SETF: >(SETF (CAR (CONS 1 nil)) 2) 2 >(setq l (CONS 1 nil)) (1) >(SETF (CAR l) 2) 2 >l (2) ;; Exercise: implement RPLACD with SETF. ;; With macros, LISP can add new special forms to suit any programmer's taste, ;; and it does. Here's UNLESS, which is a macro based on IF: >(help 'unless) ----------------------------------------------------------------------------- UNLESS [Special form and Macro] (not found "gcl.info") From ((UNLESS . Special Forms and Functions) gcl-si.info): -- Special Form: UNLESS Package:LISP Syntax: (unless test {form}*) If TEST evaluates to NIL, then evaluates FORMs as a PROGN. If not, simply returns NIL. ----------------------------------------------------------------------------- >(MACROEXPAND '(unless (x < 0) x)) (IF (NOT (X < 0)) (PROGN X)) T ;; another syntax error, eh? >(unless (x < 0) x) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by UNLESS. Condition in UNLESS [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on X: Undefined function: Broken at UNLESS. Type :H for Help. 1 Return to top level. >>1 Top level. >(unless (< x 0) x) 13 >(unless (> x 0) x) NIL ;;;;------------------------- Packages ------------------------- ;; As any modern languages, LISP must protect its namespace, and ;; also allow private namespaces for packaged library code so that ;; they don't interfere with each other, and warn of name collisions. ;; This is done through the package system with primitives such as ;; MAKE-PACKAGE, IN-PACKAGE, and USE-PACKAGE. ;; These mechanisms are described in depth in ;; http://www.flownet.com/ron/packages.pdf (brief) ;; https://www-fourier.ujf-grenoble.fr/~sergerar/Papers/Packaging.pdf (very detailed) ;; http://www.gigamonkeys.com/book/programming-in-the-large-packages-and-symbols.html (middle-ground howto) firefly:lisp user$ 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/ ;; Make a new namespace (more precisely, a table to look up symbols when evaluating them): >(MAKE-PACKAGE "foo") #<"foo" package> >(FIND-PACKAGE "foo") #<"foo" package> ;; this variable is a courtesy that any package should provide: >*package* #<"COMMON-LISP-USER" package> ;; Switch into the new symbol namespace: >(IN-PACKAGE "foo") #<"foo" package> ;; ...and this new namespace has no +! (But we just created a symbol +, unbound in this namespace): foo>(+ 1 2) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on +: Undefined function: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. foo>>1 ;; The real + is in the LISP package, and we must access it via its full package name: Top level. foo>(LISP:+ 1 2) 3 ;; Oops, no cons either! foo>(cons 1 nil) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on CONS: Undefined function: Broken at LISP:EVAL. Type :H for Help. 1 Return to top level. foo>>1 Top level. ;; ...and no nil! Indeed, this is a pristine namespace. foo>(LISP:cons 1 nil) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:EVAL. Condition in LISP:EVAL [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on NIL: Unbound variable: Broken at LISP:CONS. Type :H for Help. 1 Return to top level. foo>>1 Top level. ;; OK, this works: foo>(LISP:cons 1 LISP:nil) (1) ;; Now we want to say: if a symbol is not in this package, look it up in "LISP". But ;; we already created some new symbols here, inadvertently, and these have empty bindings. ;; These empty bindings conflict with those existing in LISP: foo>(LISP:USE- LISP:USE-PACKAGE LISP:USE-VALUE foo>(LISP:USE-PACKAGE "LISP") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:USE-PACKAGE. Condition in LISP:USE-PACKAGE [or a callee]: INTERNAL-SIMPLE-PACKAGE-ERROR: Package error on #<"foo" package>: Cannot use package as it will produce a name conflict Broken at LISP:USE-PACKAGE. Type :H for Help. 1 Return to top level. foo>>1 Top level. ;; Luckily, we can traverse the symbols we created, and nuke them with UNINTERN: ;; Except PRINT is also in LISP, not here: foo>(LISP:DO-SYMBOLS (s (LISP:FIND-PACKAGE "foo")) (print s)) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by LISP:TAGBODY. Condition in LISP:TAGBODY [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on PRINT: Undefined function: Broken at LISP:TAGBODY. Type :H for Help. 1 Return to top level. foo>>1 Top level. ;; OK, now we get the list of symbols to nuke: foo>(LISP:DO-SYMBOLS (s (LISP:FIND-PACKAGE "foo")) (LISP:print s)) + S NIL PRINT CONS LISP:NIL ;; We can nuke one... foo>(LISP:UNINTERN '+) LISP:T ;; ...it got nuked indeed: foo>(LISP:DO-SYMBOLS (s (LISP:FIND-PACKAGE "foo")) (LISP:print s)) S NIL PRINT CONS LISP:NIL ;; ...or we can automate this process: foo>(LISP:DO-SYMBOLS (s (LISP:FIND-PACKAGE "foo")) (LISP:UNINTERN s)) LISP:NIL ;; Now connecting "foo" back to "LISP" succeeds: foo>(LISP:USE-PACKAGE "LISP") T ;; ... and we can use + without explicit package name: foo>(+ 1 2) 3 ;; DO-SYMBOLS is, incidentally, a macro. It is tricky, because handling names ;; is inherently tricky (as the DEFUN example already showed): foo>(MACROEXPAND '(LISP:DO-SYMBOLS (s (LISP:FIND-PACKAGE "foo")) (LISP:UNINTERN s))) (LET ((#:G2002 (SYSTEM::COERCE-TO-PACKAGE (FIND-PACKAGE "foo"))) S #:G2004) (DOLIST (#:G2005 (CONS #:G2002 (PACKAGE-USE-LIST #:G2002)) (PROGN (SETQ S NIL) NIL)) (MULTIPLE-VALUE-BIND (#:G2008 #:G2007) (SYSTEM:PACKAGE-SIZE #:G2005) (DECLARE (FIXNUM #:G2007 #:G2008)) (IF (NOT (EQ #:G2002 #:G2005)) (SETQ #:G2007 0)) (DOTIMES (#:G2003 (+ #:G2007 #:G2008)) (SETQ #:G2004 (IF (< #:G2003 #:G2007) (SYSTEM:PACKAGE-INTERNAL #:G2005 #:G2003) (SYSTEM:PACKAGE-EXTERNAL #:G2005 (- #:G2003 #:G2007)))) #:G2006 (WHEN (NULL #:G2004) (GO #:G2009)) (SETQ S (CAR #:G2004)) (IF (OR (EQ #:G2005 #:G2002) (EQ :INHERITED (CAR (LAST (MULTIPLE-VALUE-LIST (FIND-SYMBOL (SYMBOL-NAME S) #:G2002)))))) (TAGBODY (UNINTERN S))) (SETQ #:G2004 (CDR #:G2004)) (GO #:G2006) #:G2009)))) T ;; You can pick your way through it with the Common Lisp manual. ;; I will explain only one component of it, the "gensyms", temporary symbols ;; used to avoid conflicting with any other existing variable. They are ;; produced by a special function, GENSYM. Every time it's called, it yields ;; a new name, guaranteed to be not previously used: foo>(gensym) #:G2010 foo>(gensym) #:G2011 foo>(gensym) #:G2012 foo>(gensym) #:G2013 foo>(gensym) #:G2014 foo>(gensym) #:G2015 foo>(gensym) #:G2016 foo>(gensym) #:G2017 foo>(gensym) #:G2018 ;; And we can switch to another package. Note the prompt: foo>(IN-PACKAGE "LISP") #<"LISP" package> LISP>(IN-PACKAGE "COMMON-LISP-USER") #<"COMMON-LISP-USER" package> ;;; And we check which packages the current one uses to look up symbols in (besides its own symbol table): >(PACKAGE-USE-LIST *package*) (#<"ANSI-LOOP" package> #<"DEFPACKAGE" package> #<"LISP" package>) ;;;; See packages-log.txt for more examples.