31.11. Additional Fancy Macros and Functions

31.11.1. Macro EXT:ETHE
31.11.2. Macros EXT:LETF & EXT:LETF*
31.11.3. Macro EXT:MEMOIZED
31.11.4. Macro EXT:WITH-COLLECT
31.11.6. Macro EXT:WITH-GENSYMS
31.11.7. Function EXT:REMOVE-PLIST
31.11.9. Function EXT:OPEN-HTTP and macro EXT:WITH-HTTP-INPUT
31.11.10. Variable CUSTOM:*HTTP-LOG-STREAM*
31.11.11. Function EXT:BROWSE-URL
31.11.12. Variable CUSTOM:*HTTP-PROXY*
31.11.13. Function EXT:CANONICALIZE

CLISP comes with some extension macros, mostly defined in the file macros3.lisp and loaded from the file init.lisp during make:

31.11.1. Macro EXT:ETHE

(EXT:ETHE value-type form) enforces a type check in both interpreted and compiled code.

31.11.2. Macros EXT:LETF & EXT:LETF*

These macros are similar to LET and LET*, respectively, except that they can bind places, even places with multiple values. Example:

(letf (((values a b) form)) ...)

is equivalent to

(multiple-value-bind (a b) form ...)


(letf (((first l) 7)) ...)

is approximately equivalent to

 (LET* ((#:g1 l) (#:g2 (first #:g1)))
   (UNWIND-PROTECT (PROGN (SETF (first #:g1) 7) ...)
      (SETF (first #:g1) #:g2)))

31.11.3. Macro EXT:MEMOIZED

(EXT:MEMOIZED form) memoizes the primary value of form from its first evaluation.

31.11.4. Macro EXT:WITH-COLLECT

Similar to the LOOP's COLLECT construct, except that it is looks more "Lispy" and can appear arbitrarily deep. It defines local macros (with MACROLET) which collect objects given to it into lists, which are then returned as multiple values. E.g.,

(ext:with-collect (c0 c1)
  (dotimes (i 10) (if (oddp i) (c0 i) (c1 i))))
⇒ (1 3 5 7 9) ;
⇒ (0 2 4 6 8)

returns two LISTs as multiple values.


Sometimes one may want to call an expensive function at compilation time and write the primary value into the #P".fas" file, thus speeding up loading the #P".fas" file. E.g., let your file primes.lisp be

(defun primes-list (limit)
  "Return the list of all primes smaller than LIMIT."
(defvar *all-primes* (compile-time-value (primes-list MOST-POSITIVE-FIXNUM)))


(LOAD "primes.lisp")
will not call primes-list and *all-primes* will be NIL.
(COMPILE-FILE "primes.lisp")
will call primes-list (and will probably take a long time) and will write the resulting list into (COMPILE-FILE-PATHNAME "primes.lisp")
will not call primes-list but *all-primes* will be the list computed during compilation.

An alternative is to save a memory image, which is faster than #P".fas" file but less portable.

31.11.6. Macro EXT:WITH-GENSYMS

Similar to its namesake from Paul Graham's book On Lisp, this macro is useful for writing other macros:

(with-gensyms ("FOO-" bar baz zot) ...)

expands to

(let ((bar (gensym "FOO-BAR-"))
      (baz (gensym "FOO-BAZ-"))
      (zot (gensym "FOO-ZOT-")))

31.11.7. Function EXT:REMOVE-PLIST

Similar to REMOVE and REMF, this function removes some properties from a property list. It is non-destructive and thus can be used on &REST arguments to remove some keyword parameters, e.g.,

(defmacro with-foo ((&KEY foo1 foo2) &BODY body)
  `(... ,foo1 ... ,foo2 ... ,@body))
(defmacro with-foo-bar ((&REST opts &KEY bar1 bar2
                        &BODY body)
  `(with-foo (,@(remove-plist opts :bar1 :bar2)
     ... ,bar1 ... ,bar2 ... ,@body)))
(defun foo-bar ()
  (with-foo-bar (:bar1 1 :foo2 2) ...))

here WITH-FOO does not receive the :BAR1 1 argument from FOO-BAR.


Defined in inspect.lisp, these macros are useful for the rudimentary HTTP server defined there.

31.11.9. Function EXT:OPEN-HTTP and macro EXT:WITH-HTTP-INPUT

Defined in clhs.lisp, they allow downloading data over the Internet using the HTTP protocol. (EXT:OPEN-HTTP url &KEY :IF-DOES-NOT-EXIST :LOG) opens a socket connection to the url host, sends the GET request, and returns two values: the SOCKET:SOCKET-STREAM and content length. (EXT:WITH-HTTP-INPUT (variable url) &BODY body) binds variable to the SOCKET:SOCKET-STREAM returned by EXT:OPEN-HTTP and executes the body. (EXT:WITH-HTTP-INPUT ((variable contents) url) &BODY body) additionally binds contents to the content length.

EXT:OPEN-HTTP will check CUSTOM:*HTTP-PROXY* on startup and parse the environment variable HTTP_PROXY if CUSTOM:*HTTP-PROXY* is NIL.

The :LOG argument binds CUSTOM:*HTTP-LOG-STREAM*.

31.11.10. Variable CUSTOM:*HTTP-LOG-STREAM*

Function EXT:OPEN-HTTP logs its actions to CUSTOM:*HTTP-LOG-STREAM* which is initially set to *TERMINAL-IO*.

31.11.11. Function EXT:BROWSE-URL

Function (EXT:BROWSE-URL url &KEY :BROWSER :OUT) calls a browser on the URL. browser (defaults to CUSTOM:*BROWSER*) should be a valid keyword in the CUSTOM:*BROWSERS* association list. :OUT specifies the stream where the progress messages are printed (defaults to *STANDARD-OUTPUT*).

31.11.12. Variable CUSTOM:*HTTP-PROXY*

If you are behind a proxy server, you will need to set CUSTOM:*HTTP-PROXY* to a LIST (name:password host port). By default, the environment variable http_proxy is used, the expected format is "name:password@host:port". If no #\@ is present, name and password are NIL. If no #\: is present, password (or port) is NIL.

Use function (EXT:HTTP-PROXY &OPTIONAL (STRING (EXT:GETENV "http_proxy"))) to reset CUSTOM:*HTTP-PROXY*.

31.11.13. Function EXT:CANONICALIZE

If you want to canonicalize a value before further processing it, you can pass it to EXT:CANONICALIZE together with a SEQUENCE of FUNCTIONs: (EXT:CANONICALIZE value functions &KEY (test 'EQL) (max-iter 1024)) will call functions on value until it stabilizes under test (which should be a valid HASH-TABLE-TEST) and return the stabilized value and the number of iterations the stabilization required.

E.g., clx/new-clx uses it together with XLIB:*CANONICALIZE-ENCODING* to fix the broken encoding names returned by the X Window System (e.g., convert "iso8859-1" to "ISO-8859-1") before passing them over to EXT:MAKE-ENCODING. If you encounter an EXT:ENCODING ERROR in clx/new-clx, you can augment this variable to avoid it.

These notes document CLISP version 2.49Last modified: 2010-07-07