I'm used to OO in python/java. Doing Clojure now. I came across defrecord, but it seems like I have to define a protocol for each function or set of functions I want the record to implement. Creating a new protocol creates friction. I have to name not only the function I want, but the protocol. What I'am looking for is a way to "nicely" associate a function with a record so that the function has access to the record's parameters via the this parameter, without having to define a new protocol or add a function to an existing protocol.
If you haven't tried multimethods yet, they may be closer to what you are looking for.
Define:
(defrecord Person [first middle last]) (defmulti get-name class) (defmethod get-name Person [person] (:first person))
Use:
(def captain (Person. "James" "T" "Kirk")) (get-name captain)
The multimethod implementation that is chosen is based on the dispatch function in defmulti (a function that takes the args passed to the function and returns a dispatch value). Quite commonly "class" is the dispatch function, as here, to dispatch on type. Multimethods support multiple independent ad-hoc or Java-based type hierarchies, default implementations, etc.
In general though, I think perhaps you might want to take a step back and consider whether you really want either protocols or multimethods. You seem to be trying to "do OO" in Clojure. While aspects of OO (like polymorphism) are great, maybe you should try thinking in alternate ways about your problem. For example, in the example I just gave, there is no compelling reason (yet) to implement get-name polymorphically. Why not just say:
(defn get-name [x] (:first x))
Do you even need a Person record at all? Would a simple map suffice? Sometimes the answers are yes, sometimes no.
In general Clojure does not provide class inheritance. You can certainly build an equivalent (even with protocols) if you really want it but generally I find there are other better ways of solving that problem in Clojure.
Excellent question.
As usual, there's a beautiful way to do things in Clojure - here's how to implement your own simple dynamic OO system (including inheritance, polymorphism and encapsulation) in 10 lines of Clojure.
The idea: You can put functions inside normals Clojure maps or records if you want, creating an OO-like structure. You can then use this in a "prototype" style.
; define a prototype instance to serve as your "class" ; use this to define your methods, plus any default values (def person-class {:get-full-name (fn [this] (str (:first-name this) " " (:last-name this)))}) ; define an instance by merging member variables into the class (def john (merge person-class {:first-name "John" :last-name "Smith"})) ; macro for calling a method - don't really need it but makes code cleaner (defmacro call [this method & xs] `(let [this# ~this] ((~method this#) this# ~@xs))) ; call the "method" (call john :get-full-name) => "John Smith" ; added bonus - inheritance for free! (def mary (merge john {:first-name "Mary"})) (call mary :get-full-name) => "Mary Smith"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With