2

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Daniel Shapero
  • 1,869
  • 17
  • 30

3 Answers3

5

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • Actually, in Clojure this goes beyond the namespace separation. The following works in Clojure as you would expect: `({:a "first key" :b "second key"} :a)` will yield `"first key"`, so Clojure behaves like *Lisp Machine Lisp* in this respect. So, yes, in Clojure, for some types "data is in the function namespace". "Do we want that?" Apparently, korrok's answer to this is yes, but Common Lisps answer is very much a no. JFTR, though Clojure's way of doing it enables some very concise solutions, they can at times be difficult to grasp. – schaueho Jan 17 '15 at 13:16
  • @schauerho: the question is, do we want that in Common Lisp, given its semantics? CL is a Lisp-2 and Clojure is a Lisp-1. – Rainer Joswig Jan 17 '15 at 13:45
  • I also would expect that literal data as a function in a call is not that useful, because it's relatively rare. Using bound data as a function seems to be a less rare use case... – Rainer Joswig Jan 17 '15 at 13:53
  • No, I agree that we don't. I tried to hint that I prefer Lisp-2, too, although I can understand why some people's opinion differs. – schaueho Jan 17 '15 at 13:54
  • Fset provides the @ function which either performs vector/table lookup or calls a function based on what it's given. This seems like the lisp-2 equivalent. I would argue there's still a benefit to callable tables in particular in a lisp-2: you can pass them to map and friends. – Riley May 27 '20 at 15:44
4

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.

malisper
  • 1,671
  • 1
  • 12
  • 16
  • Possible. But then `(v n)` is ten to twenty times slower than `(aref v n)`, because of the calling overhead, consing memory, ... – Rainer Joswig Jan 17 '15 at 17:16
  • @RainerJoswig I guess you could have with-callable generate marcolet and symbol-macrolet bindings that make callable objects look like functions. The local macros would expand at compile time, making (v n) equivalent to (aref v n) and the symbol-macros would still allow passing callable objects to other functions [i.e. (mapcar v '(0 1 2)) would get the first three elements]. It would be more complicated, but not by much. – malisper Jan 17 '15 at 17:38
  • 1
    you would not know in general what the type of the object is at compile time, unless you have access to compile-time type information (to see if it is an AREF, or a GETHASH, or something else...). Thus the compiler would not know that something is a simple vector and compile the access into an inline simple vector reference... – Rainer Joswig Jan 17 '15 at 17:41
  • 1
    My bad. Either that information would either have to provided explicitly (If you are going to be using aref you already know it is an array, you just have to tell that to with-callable), or it would be expanded into a generic access function. – malisper Jan 17 '15 at 17:46
0

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.

Mars
  • 8,689
  • 2
  • 42
  • 70