Michael Weber: Random Bits and Pieces

...Please don't assume Lisp is only useful for Animation and Graphics, AI, Bioinformatics, B2B and E-Commerce, Data Mining, EDA/Semiconductor applications, Expert Systems, Finance, Intelligent Agents, Knowledge Management, Mechanical CAD, Modeling and Simulation, Natural Language, Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web Authoring just because these are the only things they happened to list.

Kent M. Pitman

Lisp Logo (by Conrad Barsky)

Coming from a functional programming background, I sometimes miss referential transparency when knocking up prototypes in Lisp. For example, in Haskell I could write:


data Point = Point{ x :: Int, y :: Int } deriving (Show)

nullPoint = Point{ x = 0, y = 0 }

nextXPoint :: Point -> Point
nextXPoint p@Point{ x = x0 } = p{ x = x0 + 1 }  -- automatically create copy of p
Main> nextXPoint nullPoint
Point{x=1,y=0}
Main> nullPoint
Point{x=0,y=0}
Main> 

Behind the scenes, nextXPoint creates a shallow copy of its parameter, with adjusted field values.

In Lisp, I would need to do the copying myself, and there starts the trouble: (defstruct foo ...) automatically creates a function copy-foo, but defclass does not. So, when using CLOS I even need to write copying functions for every object myself. Quite unsatisfactory.

Fortunately, the CMU AI repository comes to the rescue with copy-objects.lisp. It even provides shallow and deep copying functions.

Unfortunately, the implementation is quite dated (1992), and so it does not run out of the box, at least not on SBCL. Kicking it a little seems to work, but I haven't tested more thoroughly yet.


--- copy_obj.cl.1	1995-06-10 01:06:43.000000000 +0200
+++ copy_obj.cl	2005-08-26 14:56:04.000000000 +0200
@@ -21,7 +21,20 @@
 
 (in-package :ut)
 
-#+allegro
+;; hack
+(cl:defpackage "CLOS"
+  #+sbcl (:use "SB-MOP")
+  #-sbcl (:use "CLOSER-MOP")
+  (:export #:class-slots
+	   #:slot-definition-allocation
+	   #:slot-definition-name
+	   #:metaobject))
+
+
+#+(or sbcl cmucl)
+(defun delete-all (set1 set2)
+  (set-difference set2 set1))
+
+#+(or allegro sbcl cmucl)
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (export '(instance-slot-names
 	    make-uninitialized-instance
@@ -30,7 +43,7 @@
 	    shallow-copy
 	    deep-copy)))
 
-#-allegro
+#-(or allegro sbcl cmucl)
 (eval-when (compile load eval)
   (export '(instance-slot-names
 	    make-uninitialized-instance
@@ -65,7 +78,7 @@
 (defmethod copy (object &key &allow-other-keys)
   (deep-copy object))
 
-#-lucid
+#-(or sbcl lucid)
 ;; class metobject is not supplied by lucid clos
 (defmethod uninitialized-copy ((object clos:metaobject) &key &allow-other-keys)
   ;; metaobjects are not copied
--- copy_obj.cl.1	1995-06-10 01:06:43.000000000 +0200
+++ copy_obj.cl	2005-08-26 14:56:04.000000000 +0200
@@ -21,7 +21,20 @@
 
 (in-package :ut)

UPDATE 2008-07-22: Cut-Down Shallow Copy

A cut-down version which produces shallow copies only, can be found here: copy-instance.lisp