Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure - mix protocol default implementation with custom implementation

In Clojure I want to have a protocol where some methods have a default implementation, and some have a custom one. And the first ones refer to the latter ones for configuration. Here is an example:

(defprotocol Saving
  (save [this] "saves to mongodb")
  (collection-name [this] "must return a string representing the associated MongoDB collection"))

;Default implementation

(extend-type Object
  Saving
  ; the `save` method is common for all, so it is actually implemened here
  (save [this] (mc/insert (collection-name [this]) this))
  ; this method is custom to every other type
  (collection-name [this] "no_collection"))

;Particular implementations

(defrecord User
  [login password]
  Saving
  (collection-name [this] "users"))

(defrecord NewsItem
  [text date]
  Saving
  (collection-name [this] "news_items"))

However, it won't work this way. Even though calling collection-name on a User or NewsItem instance returns a correct collection string, calling save on them causes an AbstractMethodError. How can I acheive this trivial OO-shaped goal with Clojure?

like image 653
noncom Avatar asked Feb 23 '13 10:02

noncom


1 Answers

Make the save function a normal function:

(defn save [obj] (mc/insert (collection-name obj) obj))

The protocol should only have collection-name

(defprotocol Saving
  (collection-name [this] "must return a string representing the associated MongoDB collection"))

And then each object that wants to be "saved" can implement this protocol.

Remember: OO style often hide the obvious simple thing :)

like image 189
Ankur Avatar answered Oct 28 '22 06:10

Ankur