Objects as functions
Normally you can't apply a non-function object to another object and get a useful result (unless the error it produces is meaningful). You have to define functions that work on objects to do something with them. Sometimes this is annoying — especially when the mapping that the function represents is not fixed.
The programming language SETL has something called maps, which are sets that contained ordered pairs. A map can be applied to an object and if the object is the first element of an ordered pair, the second element (or set of them) is returned. What's neat about SETL maps is that they can be extended easily.
$ Welcome to example program land!
f := {[1, 100], [2, 200]}; $ define f
print(f(1));
print(f(2));
f(3) := 300; $ add the pair [3, 300] to f
print(f(3));
[woz ~] $ setl -k example.setl # no REPL... (*sigh*) 100 200 300
There are a couple ways to add this kind of behaviour to Common Lisp, but the cleanest I've found uses CLOS and the MOP.
The MOP defines funcallable instances which allow objects to be called as functions. From the example given in the documentation, it's straightforward to build an object that acts like a map.
(asdf:operate 'asdf:load-op :closer-mop) (defclass setl-map () ((pairs :initarg :pairs :initform nil :accessor setl-map-pairs)) (:metaclass closer-mop:funcallable-standard-class)) (defmethod initialize-instance :after ((instance setl-map) &rest initargs) (declare (ignore initargs)) (closer-mop:set-funcallable-instance-function instance #'(lambda (x) (rest (assoc x (setl-map-pairs instance))))))
We can now go about making a function f that will act like the one from SETL. In order to add pairs, we'll define a setf function.
(defun f () nil) (setf (symbol-function 'f) (make-instance 'setl-map :pairs '((1 . 100) (2 . 200)))) (defun (setf f) (result arg) (let ((setl-map (symbol-function 'f))) (push (cons arg result) (setl-map-pairs setl-map)) result))
Now f is a function that we can call and add things to.
CL-USER> (f 1) 100 CL-USER> (f 2) 200 CL-USER> (setf (f 3) 300) 300 CL-USER> (f 3) 300
Implementing the definition of f (and its setf version) as a macro and adding the ability to remove pairs is left as an exercise to the reader.
Funcallable objects come in handy when you want (or need) to use a function as the interface to an object or a collection of objects. I generally use them when I need to define a mapping that changes over time and holding the mapping outside of the function would be cumbersome. They're a handy tool to have around.

2 comments:
The most interesting thing about SETL, in my opinion, is not that maps can be used as functions (that's really just syntactic sugar) but that the language is strictly value oriented. That is, you have variables whose values are mathematical (numbers, sets, etc.) and no way to get a reference 'inside' an object.
Languages like this have very nice semantics, with no aliasing problems. Implementing them efficiently is tricky, but some nice work was done on that at NYU, particularly by the late Prof. Paige and his coworkers.
I've been thinking that the recent buzz about 'modeling' languages should be focused on this kind of value-oriented programming language. SDL (the language, not the graphics library) is almost right, but it still has what amounts to call-by-reference.
Hi Geoff,
Did you take a look at FSet? If so, what did you think?
(Shameless plug: FSet is my set-theoretic functional collections library for Common Lisp. It was heavily influenced by Refine which in turn was influenced by SETL.)
Post a Comment