Lisp, CLOS and Math

What, you say, mathematicians just have to use Haskell ??

Stop right there, and read this.

Technically, there’s nothing new to see here at all. Lisp is high-level and low-level. Lisp is multi-paradigm. Lisp has uniform syntax. Blah blah blah. You’ve heard it all.

Still, some people seem to require an “argument from authority” (and usually still keep looking) … in which case you might be persuaded by this.

An extract (describing how defclass and defgeneric can be handily abused to yield whatever you like):

For example, a mathematician who installs under CLOS the traditional mathematical categories must organize his work as follows:

  • The defclass statements will be used to define what a “set” object is, what a “group” object is, what a “chain complex” object is, and so on.

  • The defgeneric statements will be used to define functions working on these objects, but these “functions” are traditionally called in this case functors in mathematics; therefore one defgeneric statement for the sum functor, another defgeneric for the classifying space functor, and so on.

  • Finally each generic function will have various methods to adapt the generic function to specific cases; for example the product of two objects of some category is also an object of this category with the corresponding structure to be defined. Therefore one product method for the sets, another product method for the magmas, another method for the monoids, and so on. The call-next-method and change-class functions will allow these methods to possibly refer to the less specific ones.

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.