Monthly Archives: May 2014

The less-familiar parts of Lisp for beginners — prog and prog*

Next in our survey of possibly unfamiliar Lisp features is a pair of somewhat unusual macros, prog and prog*.  These macros fall squarely into the category of “syntactic sugar”.  There’s nothing particularly special about these, they simplify the establishment of a particular sort of mini-environment.

Within prog, the programmer can declare, and optionally initialize, variables.  Then, after an optional declaration (see declare), appears a sequence of go tags and statements.  A go tag is any bare symbol or integer, not in parentheses, and is not evaluated.  A statement is enclosed in parentheses, and is evaluated when flow encounters it.  Within the statements, the programmer will typically use go to branch to a go tag in the prog body.  Execution of the prog ends when flow passes to the end of the list of statements, or if a return statement is encountered (the entire body is enclosed in an unnamed block).

The typical use case for this is a state machine.  Variables are assigned, flow enters the body, and operations are performed on data, with branches to different tags based on the values encountered.

The difference between prog and prog* is exactly like that between do and do*, or let and let*, in the variable declaration region.  The * form of the macro allows variables declared in the list to depend on earlier variables in the same declaration.

The less-familiar parts of Lisp for beginners — proclaim

We talked about declare and declaim earlier, and now we come to proclaim.  Generally, think of proclaim as being like declaim, but without any special relationship with the compiler.  In a normal context, proclaim only has an effect once the form containing it is executed, and doesn’t do anything at compile time.  The programmer might use this in a situation where declaration specifiers have to be handled at runtime, but the use cases are, in my experience, rare.

The less-familiar parts of Lisp for beginners — print-unreadable-object

Having discussed pretty-printing and print-object, now we come to a macro, print-unreadable-object, that is used to simplify the printing of objects that are deemed unreadable.  You’ve probably seen such output in the past, when printing instances of CLOS classes.  Where arrays and lists might print their contents, the Lisp printer just produces a brief output to indicate the class from which this instance was built, and an implentation-defined representation of the instance’s identity (often a memory address).

The programmer is likely to use this macro in a custom print-object method, to augment the normal printed representation of the class rather than printing the full contents.  The HyperSpec contains a good example of the use in that case.  Note that it can be used in other contexts, as follows:
*slime-repl sbcl*

CL-USER> (let ((mylist '(1 2 3)))
           (print-unreadable-object
               (mylist *standard-output* :type t :identity t)
             (format t "{{Uninteresting contents}}")))
#<CONS {{Uninteresting contents}} {1004DDE997}>
NIL

The less-familiar parts of Lisp for beginners — print-object

We’ve come to the end of pretty-printing, at least for the moment, and continue our walk through the less commonly used parts of Common Lisp with the print-object generic function.  We’ve mentioned this function briefly in passing a few times already, a good example would be this early post.

The programmer must not call the print-object method directly, but that doesn’t mean it isn’t interesting to the programmer.  The way the programmer typically interacts with this method is by specializing it on a new class or structure.  The example post linked to above does that, for the doubly-linked list structures I was reviewing at the time.

The difficulty with the doubly-linked list structure was that, if you created one with two or more elements in it, and found yourself trying to display it to the user, the internal circularity of the structure led to an infinite loop during output, which was irritating during development.  By specializing print-object on the structure, I was able to print out the contents of the list without the internal circularity being a problem.

The version of the function that I used there isn’t really complete, it’s just something short for debugging purposes.  Properly, print-object should acknowledge the special variables that control printing, and format the output appropriately.  The relevant variables are:

  • *print-readably* which should produce output that would recreate the list if presented to the Lisp reader.  Look at the earlier example for make-load-form.
  • *print-escape* of which *print-readably* is a superset, should also produce output suitable for reading into the Lisp reader.
  • *print-pretty* which should produce output intended to be more easily understood by humans, typically by the introduction of additional formatting whitespace.
  • *print-length* which indicates how many elements in a sequence should be printed before the rest of the sequence is elided.
  • *print-level* which is handled by the implementation if the print-object method obeys a simple restriction, that nested structures are handled by passing the sub-elements to write or similar output functions, not descended internally in the function itself.  This allows the printer to keep track of the level of nesting that is being printed.
  • *print-circle* which indicates that the calling context wants circularity detection to be performed, and if that requires special behaviour in the print-object method, it has to be coded to handle that case.
  • *print-radix* and *print-base* which affect the printing of rational numbers.  The programmer is unlikely to have to take special action on these variables, and can probably let them be passed down to the function responsible for printing the numeric values.
  • *print-case* which affects how symbol names are output.  Also unlikely to require special action by the programmer.
  • *print-gensym* which affects the output of uninterned symbol names, such as those returned by gensym.  Once more, the programmer is unlikely to have to write special code to respond to the value of this variable in print-object.
  • *print-array* which determines whether non-string arrays should have their contents printed, or whether a short specifier should be output instead.

The print-object generic function should return a single value, the object passed in for printing.  That is also something that was not correctly done in the earlier example.

Finally, now that we’ve covered *print-circle*, I should point out that that is another way to avoid the circularity problem printing out doubly-linked lists.  Rather than defining a print-object method, I could have done my coding in an environment where *print-circle* is non-nil.  In that case, the circularity detection allows the printer to avoid the infinite loop, and doubly-linked lists can be printed:
*slime-repl sbcl*

CL-USER> (let ((dl (dl-list:make-dl-list)))
           (dl-list:push-front dl 10) 
           (dl-list:push-front dl 20) 
           dl)
#S(DL-LIST::DL-LIST
   :FIRST-NODE #1=#S(DL-LIST::DL-NODE
                     :VALUE 20
                     :NEXT-NODE #2=#S(DL-LIST::DL-NODE
                                      :VALUE 10
                                      :NEXT-NODE NIL
                                      :PREV-NODE #1#)
                     :PREV-NODE NIL)
   :LAST-NODE #2#)

The less-familiar parts of Lisp for beginners — pprint-tab

Continuing through the commands related to pretty-printing, next is pprint-tab.  This function is used to move the effective cursor to a certain tab position, as if by the ~T argument to format.  As the use of format is not recommended inside a pprint-logical-block when *print-pretty* is non-nil, this function allows the effect of tabbing without the use of format.  Note that this function has no effect if *print-pretty* is nil.

The pprint-tab function takes a “kind” argument that determines how the tabs are calculated.

If kind is :line, the tabbing is set based on the start of the line, as if by a simple ~T argument to format.

If kind is :section, the tabbing is set based on the start of the enclosing section.

If kind is :line-relative, the tabbing is set based on the start of the line, but using relative tabulation, as defined by ~@T in format.

If kind is :section-relative, the tabbing is set based on the start of the enclosing section, again using relative tabulation as above.