Protocols for persistence - not sure about a few cases

138 views
Skip to first unread message

Jonathon McKitrick

unread,
Aug 28, 2016, 10:29:46 AM8/28/16
to Clojure
I'm beginning a foray into protocols after coming from the Common Lisp world of multimethods. I'm using them initially to implement a consistent load/save API over the DB layer.

It's pretty simple to have a Saveable protocol to save one object, because the first argument is a Record of the type I am saving.

But since protocols dispatch on the first argument, how do you define a Loadable protocol that loads an object or handles a collection of objects?

For example, if I want to load an object by idx, the first argument is not a Record, but an integer, or perhaps an arbitrary field to used in a query.


Shantanu Kumar

unread,
Aug 28, 2016, 3:27:26 PM8/28/16
to Clojure
Considering the regular use-cases with records:

Create - requires record without any auto-generated identifiers
Retrieve - requires primary identifier or lookup parameters
Update - requires record with primary identifier and updated fields
Delete - requires primary identifier or lookup parameters

Retrieve and Delete and quite similar, except that `Retrieve` returns record(s). Create and Update are similar, except that `Update` requires only the updated fields. It is clear that one record type cannot expose all of the CRUD operations. Could it be possible that protocols/records are a wrong axis to think about this problem?

Can you share some details about the type of database and the access use cases you are considering?


Shantanu
Message has been deleted

Jonathan Fischer

unread,
Aug 29, 2016, 5:09:23 PM8/29/16
to Clojure
The most straightforward one: just make your protocols a thing that your storage medium implements, instead of your records.  E.g.:

(defprotocol ThingStore
  (load-item [store item-id])
  (load-items [store item-ids])
  (find-item [store item-name])
  (save-item [store item]))

(defrecord DatabaseStore
  ThingStore
  (load-item [store item-id] ...))

Otherwise, Clojure does have multimethods, but the dispatch mechanism doesn't work the same was as Common Lisp's.  You could probably combine multimethods with a marker protocol to do what you're trying.

;; First, an empty marker protocol
(defprotocol Loadable)

;; And something that you want to load
(defrecord Thing [data]
  Loadable ;; mark that it implements that empty protocol
)

;; Define a multimethod dispatch function that will figure out the right implementation to call
(defn loadable-dispatch
  [item]
  (cond (satisfies? Loadable item) ::load-one-item
       (and (sequential? item) (every? #(satisfies? Loadable %) item)) ::load-many-items
       :else nil))

;; The multimethod itself
(defmulti load-item loadable-dispatch)

;; And implementations
(defmethod load-item ::load-one-item
  [item]
  "Load one item.")

(defmethod load-item ::load-many-items
  [items]
  "Load many items.")

user> (load-item (map->Thing {}))
"Load one item."

user> (load-item [(map->Thing {}) (map->Thing {})])
"Load many items."

Overall, you're probably better off with the first option, but you can make multimethods work for you here if you want.

jmcki...@gmail.com

unread,
Aug 29, 2016, 5:11:46 PM8/29/16
to Clojure
Makes perfect sense. Thanks!

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/LBQ_AELp0Mo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
Jonathon McKitrick

jmcki...@gmail.com

unread,
Aug 29, 2016, 5:18:53 PM8/29/16
to Clojure
I read too quickly. I might need multi-methods after all, just like I used in Common Lisp. I want different types of persisted objects to have the same interface, if possible.

Actually, I'm just experimenting with protocols, so I'm probably better off looking for a different use case.
--
Jonathon McKitrick
Reply all
Reply to author
Forward
0 new messages