Can I write function & object things in Clojure?

For example, maps, sets, and vectors can each be used either as functions or as data objects (meaning they can be the first item or any other item of a form). Was this functionality provided via Clojure? If so, how an I define something that operates the same way?

In my case, as a learning exercise I want to define a “Matrix” thing that can either be viewed as a data structure itself or used as a transformation upon appropriate things.

Yes, you can hook into this yourself. Clojure can invoke anything that implements the clojure.lang.IFn interface. If you want to explicitly be able to invoke it you can implement IFn and the invoke arities that are important to you. For something like a Matrix, you probably want a deftype.

For example, if you wanted to create a Rectange type and be able to invoke it to get it’s area (this is a totally arbitrary example):

(deftype Rectangle [x y]
  clojure.lang.IFn
  (invoke [_] (* x y)))

(def rect (->Rectangle 4 5))
(rect) ;; 20
3 Likes

Perfect! Thanks; this is a level of interop I haven’t done
before. Is this cljc compatible, or does it break outside of the
jvm?

You can use three backticks ``` in the beginning and end of an enclosing block of code and you will have syntax highlighting in your code :

(deftype Rectangle [x y]
  clojure.lang.IFn
  (invoke [_] (* x y)))

(def rect (->Rectangle 4 5))
(rect) ;; 20

You can do the same thing in ClojureScript, but the syntax is a little different unfortunately. Use IFn instead of clojure.lang.IFn, and add a dash to invoke.

Quickly tried it out with Lumo:

$ lumo
cljs.user=> (deftype Rectangle [x y]
       #_=>   IFn
       #_=>   (-invoke [_] (* x y)))
cljs.user/Rectangle
cljs.user=> 
cljs.user=> (def rect (->Rectangle 4 5))
#'cljs.user/rect
cljs.user=> (rect)
20

Thanks for the help. I have working CLJ code (still need to do the work of making it fully CLJC compatible). I found I had to implement several type functions to get it to play nice with things like my matrix multiplication function, so I needed to implement Counted and ISeq in addition to IFn. Here’s what I ended up with (and it works):

(defprotocol M
  (rows [m])
  (columns [m]))

(deftype Matrix [A]
  M
  (rows [_] (get-rows A))
  (columns [_] (get-columns A))
  
  clojure.lang.ISeq
  (seq [_] (seq A))

  clojure.lang.Counted
  (count [_] (count A))
  
  clojure.lang.IFn
  (invoke [_ B] (m* A B)))

It feels like there’s probably a shorter way of subscribing to other types than line-by-line definitions of things like seq and count. Is there a simpler way?

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.