Search code examples
clojurecommon-lisplisp-2callable-object

emulating Clojure-style callable objects in Common Lisp


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.


Solution

  • 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.