Monthly Archives: March 2014

The less-familiar parts of Lisp for beginners — make-broadcast-stream

Next, we come to make-broadcast-stream.  This isn’t really a difficult concept to understand, but I highlight it because it’s not a primitive that appears in the C library, so a new Lisp programmer coming from C++ might not realize that this tool is available.  This function creates an output stream that transparently splits its data streams out into one or more target streams.  The most obvious uses for this are for setting up a mode that echos data to the screen as well as its normal output target, or for creating a unified transcript of client and server messages for debugging purposes.  Naturally, any other context where you want to send the same data to multiple output streams also benefits from this function.

Earlier in this series I skipped broadcast-stream-streams as I was waiting to include it with make-broadcast-stream.  The broadcast-stream-streams function returns a list of the streams to which the broadcast stream sends its output.

The less-familiar parts of Lisp for beginners — macrolet

We now make a brief stop at macrolet.  This is the macro-equivalent of labels/flet.  It creates a locally defined macro while not interning its name.  There are a couple of things to be careful of.  First, unlike labels and flet, macrolet does not have access to the local variables in the lexical environment.  The second thing to note is that macrolet more closely resembled labels, in that its definitions are visible inside its own body (i.e. macrolet macros can invoke themselves recursively).

To show how defmacro works in practice, here’s a short fragment:
 

(defun myfunc (x dir)
  (macrolet
      ((incdec (y dir)
         (cond
           ((keywordp dir)
            (format t "kw~%")
            (ecase dir
              (:PLUS
               `(1+ ,y))
              (:MINUS
               `(1- ,y))))
           (t
            (format t "non-kw~%")
            `(ecase ,dir
               (:PLUS
                (incdec ,y :PLUS))
               (:MINUS
                (incdec ,y :MINUS)))))))

    (format t "~A~%" (incdec x :PLUS))
    (format t "~A~%" (incdec x :MINUS))
    (format t "~A~%" (incdec x dir))))

When I say that the macro does not have access to the local variables, I mean that it is not possible to use x or ,x in the body of the macro, those are unavailable.

The output upon load is as follows:
 

CL-USER> (load "macrolet")
kw
kw
non-kw
kw
kw
T

Let’s review that output at load time.  I’ve added format statements in the macrolet, outside of the backticked region, so we can see how the macro is being expanded.  We define the incdec macro so that it examines its invocation context.  If the form containing the incdec call has a literal keyword object, such as :PLUS or :MINUS, the macro expands to a simple 1+ or 1- call, respectively.  If, on the other hand, the invocation has a variable, or some other form which is not known at compile time to reduce to a keyword, then the macro inserts code to examine the variable at run time and take the appropriate action, recursively calling incdec with the fixed keyword to avoid infinite recursion.

When the function is loaded, it first sees the (incdec x :PLUS) form, so it substitutes the keyword version of the macro.  The next form contains :MINUS there, and also substitutes the keyword version.  The third line, though, uses dir, which is passed as a parameter, so its value is not available at compile/load time.  That causes the macro to expand in the non-keyword version, which itself expands into two keyword versions of the macro.

The less-familiar parts of Lisp for beginners — macro-function

Next, we arrive at macro-function.  This accessor allows the programmer to read or modify the function field of a symbol in the context of macros.  Recall from this earlier discussion that a symbol has five associated fields, one of which is a function field.  In fact, the function field can reference either a function or a macro.  The fdefinition accessor reads or modifies this field in the general case, while macro-function limits its operations to the context of macros.

If macro-function is invoked on a symbol name that is not an interned symbol, or on a symbol whose function field references a function, then it returns nil.  On the other hand, if fdefinition is invoked on a symbol name or object that is not interned, it raises a condition of type undefined-function.  Here is a transcript of the cases:
[/raw]

 

CL-USER> (defun my-function (x) (+ 1 x))
MY-FUNCTION
CL-USER> (defmacro my-macro (x) `(list 1 ,x))
MY-MACRO
CL-USER> (fdefinition 'my-function)
#<FUNCTION MY-FUNCTION>
CL-USER> 
(fdefinition 'my-macro)
#<CLOSURE (LAMBDA (&REST SB-C::ARGS) :IN MACRO-FUNCTION) {1006CDBFCB}>
CL-USER> (fdefinition 'non-existent-symbol)
; Evaluation aborted on #<UNDEFINED-FUNCTION NON-EXISTENT-SYMBOL {1002E4D3E3}>.
CL-USER> (let ((sym (make-symbol "ABC")))
           (setf (fdefinition sym) (fdefinition 'my-function))
           (fdefinition sym))
#<FUNCTION MY-FUNCTION>
CL-USER> (macro-function 'my-function)
NIL
CL-USER> (macro-function 'my-macro)
#<FUNCTION (MACRO-FUNCTION MY-MACRO) {1005A3A46B}>
CL-USER> (macro-function 'non-existent-symbol)
NIL

[/raw]

The less familiar parts of Lisp for beginners — summary G, H, I, L

I’ve written a few posts about features of the Lisp language that a newcomer arriving from C++ might not have encountered.  I’m going through them alphabetically, so here is a summary page of the functions beginning with the letters ‘G’, ‘H’, ‘I’, and ‘L’:

gensym

get-dispatch-macro-character

handler-bind and handler-case

initialize-instance

intern

lambda

ldb

ldiff and tailp

load-time-value

locally