Defstruct and Defclass as macros

The SBCL version

(macroexpand-1 '(defstruct spoint (x y)))

(PROGN
 (SB-KERNEL:WITH-SINGLE-PACKAGE-LOCKED-ERROR (:SYMBOL 'SPOINT
                                              "defining ~A as a structure"))
 (SB-KERNEL::%DEFSTRUCT '#
                        '#(#
                           #)
                        (SB-C:SOURCE-LOCATION))
 (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE)
   (SB-KERNEL::%COMPILER-DEFSTRUCT '#
                                   '#(#
                                      #)))
 (DECLAIM (FTYPE (SB-INT:SFUNCTION (&KEY (:X T)) SPOINT) MAKE-SPOINT))
 (DEFUN MAKE-SPOINT (&KEY ((:X #:DUM947) Y))
   (SB-KERNEL::%MAKE-STRUCTURE-INSTANCE-MACRO
    # '((:SLOT T . 1)) #:DUM947))
 (LOCALLY
  (DECLARE (NOTINLINE SB-KERNEL:FIND-CLASSOID))
  (SETF (SB-KERNEL::STRUCTURE-CLASSOID-CONSTRUCTOR
         (SB-KERNEL:FIND-CLASSOID 'SPOINT))
          #'MAKE-SPOINT))
 'SPOINT)

(macroexpand-1 '(defclass point () (x y)))

(PROGN
 (EVAL-WHEN (:COMPILE-TOPLEVEL)
   (SB-PCL::%COMPILER-DEFCLASS 'POINT 'NIL 'NIL '(Y X)))
 (EVAL-WHEN (:LOAD-TOPLEVEL :EXECUTE)
   (LET ()
     (SB-PCL::LOAD-DEFCLASS 'POINT 'STANDARD-CLASS 'NIL
                            (LIST
                             (LIST* :NAME 'X :READERS 'NIL :WRITERS 'NIL
                                    :INITARGS 'NIL 'NIL)
                             (LIST* :NAME 'Y :READERS 'NIL :WRITERS 'NIL
                                    :INITARGS 'NIL 'NIL))
                            (LIST :DIRECT-DEFAULT-INITARGS NIL) 'NIL 'NIL
                            '(Y X) (SB-C:SOURCE-LOCATION) 'NIL))))

The Allegro CL version

(macroexpand-1 '(defstruct spoint (x y)))

(PROGN (PROGN (EVAL-WHEN (COMPILE)
                (EXCL::CHECK-LOCK-DEFINITIONS-COMPILE-TIME 'SPOINT :TYPE 'DEFSTRUCT (SYSTEM:CE-GET 'SPOINT 'EXCL::%STRUCTURE-DEFINITION)))
              (EVAL-WHEN (LOAD EVAL) (EXCL::CHECK-LOCK-DEF-DEFSTRUCT 'SPOINT)) (RECORD-SOURCE-FILE 'SPOINT :TYPE :TYPE))
       (PROGN (EVAL-WHEN (COMPILE) (PUSH 'SPOINT-X EXCL::.FUNCTIONS-DEFINED.)) (RECORD-SOURCE-FILE 'SPOINT-X)
              (SETF (SYMBOL-FUNCTION 'SPOINT-X) (EXCL::GET-BUILT-IN-ACCESSOR 1)))
       (DEFSETF SPOINT-X EXCL::DEFSTRUCT-SLOT-DEFSETF-HANDLER 1) (DEFUN MAKE-SPOINT (&KEY (#:X Y)) (SYSTEM::NEW-STRUCT 'SPOINT #:X))
       (DEFINE-COMPILER-MACRO MAKE-SPOINT (&WHOLE EXCL::WHOLE &REST REST)
         (LET ((EXCL::INITS #)) (IF EXCL::INITS (EXCL::BQ-LIST `LET* EXCL::INITS #) EXCL::WHOLE)))
       (EXCL::DEFINEF COPY-SPOINT (SYMBOL-FUNCTION 'COPY-STRUCTURE)) (EXCL::DEFINEF SPOINT-P (EXCL::GET-DD-PREDICATE-CLOSURE 'SPOINT))
       (EVAL-WHEN (LOAD EVAL)
         (LET ((EXCL::NEW #))
           (EXCL::COMPUTE-STRUCT-CPL EXCL::NEW)
           (EXCL::NOTIFY-CLOS-OF-NEW-STRUCTURE 'SPOINT EXCL::NEW)
           (EVAL-WHEN (LOAD EVAL) (SETF # EXCL::NEW))))
       (EVAL-WHEN (COMPILE)
         (LET ((EXCL::OLD #) (EXCL::NEW #))
           (SETF (EXCL::DD-INCLUDED-BY EXCL::NEW) (IF EXCL::OLD #))
           (EXCL::COMPUTE-STRUCT-CPL EXCL::NEW)
           (EXCL::NOTIFY-CLOS-OF-NEW-STRUCTURE 'SPOINT EXCL::NEW SYSTEM:*COMPILATION-UNIT-ENVIRONMENT*)
           (SYSTEM:CE-PUTPROP 'SPOINT EXCL::NEW 'EXCL::%STRUCTURE-DEFINITION)))
       ...)

(macroexpand-1 '(defclass point () (x y)))

(PROGN NIL (EVAL-WHEN (COMPILE) (EXCL::CHECK-LOCK-DEFINITIONS-COMPILE-TIME 'POINT :TYPE 'DEFCLASS (FIND-CLASS 'POINT NIL)))
       (RECORD-SOURCE-FILE 'POINT :TYPE :TYPE)
       (EXCL::ENSURE-CLASS-1 'POINT :DIRECT-SUPERCLASSES 'NIL :DIRECT-SLOTS (LIST (LIST ':NAME 'X) (LIST ':NAME 'Y))))

C and C++ are firmly stuck in the early 80’s as far as processor design is concerned. Common Lisp never was stuck anywhere in the first place, which means it has had a hard time sticking to something in particular, as well, but at least it has the uncommon ability to let go.

Fixing a SLIME/Swank version problem

Installing a bunch of packages willy-nilly resulted in this configuration problem for me: starting slime resulted in this frustrating error message: Versions differ (2014…) slime vs (2009…) swank

It asks you whether to continue. If you say ‘n’, sorry, no slime for you ! If you say ‘y’, no fancy slime for you; you have to be content with the Comint buffer mode

If you want to fix this, you’re going to have to fix a possible mismatch somewhere.

In my case, I ran locate swank-loader.lisp and found two instances: one under /usr/share/common-lisp/... and the other under /usr/share/emacs24/site-lisp/..., you will probably have different paths but similarly conflicting.

Running dpkg -S on the first, I found it came from the cl-swank package, so I uninstalled it.

Now, slime refused to start because it was trying to load the missing swank-loader.lisp file.

Luckily, this was simply a matter of running M-x customize-variable swank-backend and set it to the remaining path (e.g. in my case, "/usr/share/emacs24/site-lisp/google/slime/swank-loader.lisp")

After this, M-x slime worked as usual!

Defstruct vs Defclass: a 2x difference

I created a million instances of each (the structure spoint, and the class point), with randomized members, and the former took about 50 milliseconds, while the latter took about 100 milliseconds. Without obsessing over absolute numbers (this is a reasonable recent linux box), I believe the takeaway is that you should avoid used heavy CLOS until you really need to.

CL-USER> (defstruct spoint xp yp)
SPOINT
CL-USER> (time
(dotimes (i 1000000)
(make-spoint :xp (random 1000000) :yp (random 1000000))))
Evaluation took:
0.054 seconds of real time
0.050000 seconds of total run time (0.050000 user, 0.000000 system)
92.59% CPU
172,978,648 processor cycles
31,981,568 bytes consed

CL-USER> (defclass point () ((xp :initarg :xp) (yp :initarg :yp)))
#<STANDARD-CLASS POINT>
CL-USER> (time
(dotimes (i 1000000)
(make-instance 'point :xp (random 1000000) :yp (random 1000000))))
Evaluation took:
0.102 seconds of real time
0.100000 seconds of total run time (0.100000 user, 0.000000 system)
[ Run times consist of 0.020 seconds GC time, and 0.080 seconds non-GC time. ]
98.04% CPU
16 lambdas converted
324,534,044 processor cycles
64,606,960 bytes consed

Update: These figures are from SBCL, but I tried the same on AllegroCL and got identical results.

Of course the other takeaway is that everything is really really fast.

So much for the AllegroCL trial

I suppose it’s worth learning anyway, it seems to give a very precise control over compilation, has a bunch of libraries and is very well documented. Also, an email to support@franz.com, which turned out to be a very silly question, was answered promptly. One day, I’m sure I will buy the Professional version!

However, I can’t always use this as an experimental Common Lisp platform. Today I wanted to try out Genworks, but when I tried to load it, I saw

Error:
An explicit gc call caused tenuring and a need for 786432 more bytes of heap.
This request cannot be satisfied because you have hit the Allegro CL Free Express heap limit.
The Student Edition has an unlimited heap.
For more information on the Student Edition, see http://www.franz.com/downloads/student.lhtml.
For an evaluation of our Enterprise or Professional Editions please contact us at sales@franz.com or (510) 452-2000.
    [condition type: STORAGE-CONDITION]

I don’t want to pretend to be a student, so … back to SBCL!

AllegroGraph: Getting Started

I’m following along with Mark Watson’s excellent book on using AllegroGraph, but the automated install mentioned at the beginning didn’t work for me.

While I figure this out, the following manual version does work:

Install the server

  • Download and extract the tar.gz file

  • ./agraph-4.13.1/install-agraph ~/Lisp/AllegroGraph

  • ./AllegroGraph/bin/agraph-control --config AllegroGraph/lib/agraph.cfg start

Add a test user

(say user:test, password:xyzzy)

Install the client

  • Download and extract the tar.gz file

  • :ld $home/Lisp/agraph-4.13.1-linux86-acl90-client-lisp-acl9.0/agraph4.fasl

  • (in-package :db.agraph.user)

  • (create-triple-store "tmp-rdfstore-1"
    :user "test"
    :password "xyzzy")

Note: the example from the book uses /ns/name, but this is not allowed

  • (enable-!-reader)

Note: the book omits this at the beginning, but this is essential to do what comes next (without this, something like !owl is treated like a package name)

  • (add-triple !owl:cat !owl:eats !owl:fish)

(get-triple-by-id 1)
<cat eats fish>

Enjoy! More to follow, in fits and starts …