Category Archives: Uncategorized

The less-familiar parts of Lisp for beginners — throw

I’m going to talk a bit now about catch and throw.  These aren’t really obscure, particularly to somebody coming from a C++ background, who probably goes looking for these keywords when wanting to find out about throwing exceptions.  I talked at length about exceptions in Lisp earlier, and skipped over these, as they’re not really interesting or novel, but they deserve a mention.

So, catch and throw.  Similar in concept to the operations in C++, but rather than the catch specializing on objects, catch specifies a label, and throw jumps to the named label.  Consequently, you don’t have cascading catch directives, as you might have in C++.  If there is no matching catch for the throw, an error is signaled.

The less-familiar parts of Lisp for beginners — the

The newcomer to Lisp might not have seen the special operator the before.  This special operator allows the programmer to inform the compiler of the type returned by a form, in the event that the compiler cannot deduce it from context.  Note that this is not the same as a C or C++ static cast, the does not modify a type, and does not necessarily perform type checking (behaviour is undefined if you present it an object that is incompatible with the type declared by the), it merely tells the compiler what it should expect to get back from a form.  Compare this with the declare special operator, which can be used to tell the compiler what the types are of parameters passed to a function.

To demonstrate, I’ve written a function, remote-function, that simply returns the number 20.  I then have some code that uses this function, which I’m compiling for speed.  The compiler doesn’t know what remote-function does, and I could always change the function after compilation, so it can’t optimize fully without the the special operator.  The resulting disassembly listings show the difference.  In the first case, without the, the addition operator is a function call to generic-+, whereas in the second case the addition is made using straight integer arithmetic, with no function setup and call.

Without the special operator compiler hint:
the.lisp

(declaim (optimize (debug 0) (safety 0) (speed 3)))

(defun my-adder ()
  (+ 5 (remote-function)))

*slime-repl sbcl*
CL-USER> (disassemble 'my-adder)
; disassembly for MY-ADDER
; Size: 51 bytes
; 033CEADF:       488D5424F0       LEA RDX, [RSP-16]          ; no-arg-parsing entry point
;      AE4:       4883EC18         SUB RSP, 24
;      AE8:       488B05A9FFFFFF   MOV RAX, [RIP-87]          ; #<FDEFINITION object for REMOTE-FUNCTION>
;      AEF:       31C9             XOR ECX, ECX
;      AF1:       48892A           MOV [RDX], RBP
;      AF4:       488BEA           MOV RBP, RDX
;      AF7:       FF5009           CALL QWORD PTR [RAX+9]
;      AFA:       480F42E3         CMOVB RSP, RBX
;      AFE:       BF0A000000       MOV EDI, 10
;      B03:       41BBF0010020     MOV R11D, 536871408        ; GENERIC-+
;      B09:       41FFD3           CALL R11
;      B0C:       488BE5           MOV RSP, RBP
;      B0F:       F8               CLC
;      B10:       5D               POP RBP
;      B11:       C3               RET
NIL

With the special operator compiler hint:
the.lisp
(declaim (optimize (debug 0) (safety 0) (speed 3)))

(defun my-adder ()
  (+ 5 (the (integer 0 65535) (remote-function))))

*slime-repl sbcl*
CL-USER> (disassemble 'my-adder)
; disassembly for MY-ADDER
; Size: 41 bytes
; 0379FF5F:       488D5424F0       LEA RDX, [RSP-16]          ; no-arg-parsing entry point
;       64:       4883EC18         SUB RSP, 24
;       68:       488B05A9FFFFFF   MOV RAX, [RIP-87]          ; #<FDEFINITION object for REMOTE-FUNCTION>
;       6F:       31C9             XOR ECX, ECX
;       71:       48892A           MOV [RDX], RBP
;       74:       488BEA           MOV RBP, RDX
;       77:       FF5009           CALL QWORD PTR [RAX+9]
;       7A:       480F42E3         CMOVB RSP, RBX
;       7E:       4883C20A         ADD RDX, 10
;       82:       488BE5           MOV RSP, RBP
;       85:       F8               CLC
;       86:       5D               POP RBP
;       87:       C3               RET
NIL

The less-familiar parts of Lisp for beginners — symbol-package

The symbol-package function can be used to locate the original package in which a symbol is defined.  Because it is possible to import symbols into other packages, it may sometimes not be clear what the origin is of a symbol being used.  Here is a transcript that demonstrates the use of symbol-package.  In it, we create a package, and a function in that package.  We then create a second package and import the symbol corresponding to the function.  Outside of either package, we can invoke the function in the second package, but symbol-package reveals that it is actually found in the first.
*slime-repl sbcl*

CL-USER> (defpackage :base (:use :common-lisp))
#<PACKAGE "BASE">
CL-USER> (in-package :base)
#<PACKAGE "BASE">
BASE> (defun myfunc () (format t "hi~%"))
MYFUNC
BASE> (defpackage :derived (:use :common-lisp))
#<PACKAGE "DERIVED">
BASE> (in-package :derived)
#<PACKAGE "DERIVED">
DERIVED> (import 'base::myfunc)
T
DERIVED> (myfunc)
hi
NIL
DERIVED> (in-package :cl-user)
#<PACKAGE "COMMON-LISP-USER">
CL-USER> (derived::myfunc)
hi
NIL
CL-USER> (symbol-package 'derived::myfunc)
#<PACKAGE "BASE">

The less-familiar parts of Lisp for beginners — symbol-macrolet

Our next obscure Lisp feature is the symbol-macrolet special operator.  This operator produces a context in which one or more symbols can be rewritten prior to evaluation.  While let, and similar operators, can shadow it, outside of that exception, the symbols are substituted according to the expansions supplied.

Here is some code to demonstrate the behaviour:
symbol-macrolet.lisp

(defun demonstrate ()
  (let ((replacement-1 'my-new-fcn-1))
    (symbol-macrolet
        ((my-fcn-1 replacement-1)
         (my-fcn-2 'rpl-2))
      (format t 
              "(my-fcn-1 my-fcn-2): ~A~%"
              (list my-fcn-1 my-fcn-2))
      (format t "Now, binding to shadow my-fcn-2~%")
      (multiple-value-bind (result my-fcn-2)
          (floor 10.4 1.2)
        (format t 
                "(my-fcn-1 my-fcn-2): ~A~%"
                (list my-fcn-1 my-fcn-2))))))

with output:
*slime-repl sbcl*
CL-USER> (demonstrate)
(my-fcn-1 my-fcn-2): (MY-NEW-FCN-1 RPL-2)
Now, binding to shadow my-fcn-2
(my-fcn-1 my-fcn-2): (MY-NEW-FCN-1 0.79999924)
NIL