emulating Clojure-style callable objects in Common Lisp

541 Views Asked by At

In Clojure, hash-maps and vectors implement invoke, so that they can be used as functions, for example

(let [dict {:species "Ursus horribilis"
            :ornery :true
            :diet "You"}]
  (dict :diet))

lein> "You"

or, for vectors,

(let [v [42 613 28]]
  (v 1))

lein> 613

One can make callable objects in Clojure by having them implement IFn. I'm new-ish to Common Lisp -- are callable objects possible and if so what would implementing that involve? I'd really like to be able to do things like

(let ((A (make-array (list n n) ...)))
   (loop for i from 0 to n
         for j from 0 to m
      do (setf (A i j) (something i j)))
   A)

rather than have code littered with aref. Likewise, it would be cool if you could access entries of other data structures, e.g. dictionaries, the same way.

I've looked at the wiki entry on function objects in Lisp/Scheme and it seems as if having a separate function namespace will complicate matters for CL, whereas in Scheme you can just do this with closures.

3

There are 3 best solutions below

5
On BEST ANSWER

Example of callable objects in a precursor of Common Lisp

Callable objects have been provided before. For example in Lisp Machine Lisp:

Command: ("abc" 1)            ; doesn't work in Common Lisp
#\b

Bindings in Common Lisp

Common Lisp has separate namespaces of names for functions and values. So (array 10 1 20) would only make sense, when array would be a symbol denoting a function in the function namespace. Thus the function value then would be a callable array.

Making values bound to variables act as functions mostly defeats the purpose of the different namespaces for functions and values.

(let ((v #(1 2 3)))          
  (v 10))                    ; doesn't work in Common Lisp

Above makes no sense in a language with different namespaces for functions and values.

FLET is used for functions instead of LET.

(flet ((v #(1 2 3 4 5 6 7))) ; doesn't work in Common Lisp
  (v 4))                     

This would then mean we would put data into the function namespace. Do we want that? Not really.

Literal data as functions in function calls.

One could also think of at least allowing literal data act as functions in direct function calls:

(#(1 2 3 4 5 6 7) 4)         ; doesn't work in Common Lisp

instead of

(aref #(1 2 3 4 5 6 7) 4)

Common Lisp does not allow that in any trivial or relatively simple way.

Side remark:

One can implement something in the direction of integrating functions and values with CLOS, since CLOS generic functions are also CLOS instances of the class STANDARD-GENERIC-FUNCTION and it's possible to have and use user-defined subclasses of that. But that's usually not exploited.

Recommendation

So, best to adjust to a different language style and use CL as it is. In this case Common Lisp is not flexible enough to easily incorporate such a feature. It is general CL style to not omit symbols for minor code optimizations. The danger is obfuscation and write-only code, because a lot of information is not directly in the source code, then.

0
On

I agree with Rainer Joswig's advice: It would be better to become comfortable with Common Lisp's way of doing things--just as it's better for a Common Lisp programmer to become comfortable with Clojure's way of doing things, when switching to Clojure. However, it is possible to do part of what you want, as malisper's sophisticated answer shows. Here is the start of a simpler strategy:

(defun make-array-fn (a) 
  "Return a function that, when passed an integer i, will 
  return the element of array a at index i."
  (lambda (i) (aref a i)))

(setf (symbol-function 'foo) (make-array-fn #(4 5 6)))

(foo 0) ; => 4
(foo 1) ; => 5
(foo 2) ; => 6

symbol-function accesses the function cell of the symbol foo, and setf puts the function object created by make-array-fn into it. Since this function is then in the function cell, foo can be used in the function position of a list. If you wanted, you could wrap up the whole operation into a macro, e.g. like this:

(defmacro def-array-fn (sym a)
  "Define sym as a function that is the result of (make-array-fn a)."
  `(setf (symbol-function ',sym)
         (make-array-fn ,a)))

(def-array-fn bar #(10 20 30 40))

(bar 0) ; => 10
(bar 1) ; => 20
(bar 3) ; => 40

Of course, an "array" defined this way no longer looks like an array. I suppose you could do something fancy with CL's printing routines. It's also possible to allow setting values of the array as well, but this would probably require a separate symbols.

4
On

Although there may not be a way to do exactly what you want to do, there are some ways to hack together something similar. One option is define a new binding form, with-callable, that allows us to bind functions locally to callable objects. For example we could make

(with-callable ((x (make-array ...)))
  (x ...))

be roughly equivalent to

(let ((x (make-array ...)))
  (aref x ...))

Here is a possible definition for with-callable:

(defmacro with-callable (bindings &body body)
  "For each binding that contains a name and an expression, bind the
   name to a local function which will be a callable form of the
   value of the expression."
  (let ((gensyms (loop for b in bindings collect (gensym))))
    `(let ,(loop for (var val) in bindings
                 for g in gensyms
                 collect `(,g (make-callable ,val)))
       (flet ,(loop for (var val) in bindings
                    for g in gensyms
                    collect `(,var (&rest args) (apply ,g args)))
         ,@body))))

All that's left is to define different methods for make-callable that return closures for accessing into the objects. For example here is a method that would define it for arrays:

(defmethod make-callable ((obj array))
  "Make an array callable."
  (lambda (&rest indices)
    (apply #'aref obj indices)))

Since this syntax is kind of ugly we can use a macro to make it prettier.

(defmacro defcallable (type args &body body)
  "Define how a callable form of TYPE should get access into it."
  `(defmethod make-callable ((,(car args) ,type))
     ,(format nil "Make a ~A callable." type)
     (lambda ,(cdr args) ,@body)))

Now to make arrays callable we would use:

(defcallable array (obj &rest indicies)
  (apply #'aref obj indicies))

Much better. We now have a form, with-callable, which will define local functions that allow us to access into objects, and a macro, defcallable, that allows us to define how to make callable versions of other types. One flaw with this strategy is that we have to explicitly use with-callable every time we want to make an object callable.


Another option that is similar to callable objects is Arc's structure accessing ssyntax. Basically x.5 accesses the element at index five in x. I was able to implement this in Common Lisp. You can see the code I wrote for it here, and here. I also have tests for it so you can see what using it looks like here.

How my implementation works is I wrote a macro w/ssyntax which looks at all of the symbols in the body and defines macros and symbol-macros for some of them. For example the symbol-macro for x.5 would be (get x 5), where get is a generic function I defined that accesses into structures. The flaw with this is I always have to use w/ssyntax anywhere I want to use ssyntax. Fortunately I am able to hide it away inside a macro def which acts like defun.