tags:

views:

166

answers:

2

In a number of occasions I have a collection of functions that I'd like to implement in different ways. The most obvious example of this would be to abstract from specific databases. In an object-oriented language you would use an interface for this:

interface DB {
  ResultSet query(String query);
  void persist(Object o);
  ...
}

In speudo code I would want to do something like this:

(ns dbbackend)

(abstractfn query [q])
(abstractfn persist! [o])

And then implementations for each database:

(ns dbbackend.mysql :implements dbbackend)
(defn query [q] ...)
(defn persist! [o] ...)

It is not entirely clear to me what the best practice is to do something similar in a functional language, specifically Clojure. Should I use multi-methods for this?

+7  A: 

Now that version 1.1 of Clojure has been released maybe it's time to take a look into the future.

Datatypes and protocols, which are currently only available in the new master branch on github , might be exactly what you are looking for.

(defprotocol DB
  (query   [backend query])
  (persist [backend object]))

(deftype MySQLBackend []
  DB
  (query   [query] ...)
  (persist [object] ...))
Jonas
+1 for the interesting info, but ouch, my brain hurts! Clojure seems to be growing faster than I can learn it :(
Carl Smotricz
I read about those, but assumed they were mostly optimization (Clojure in Clojure etc.). They seem to fit here well too, though. Thanks.
Zef Hemel
+2  A: 

For pre-protocol Clojure versions:

The interface:

(ns dbbackend)

(defmulti query
  {:arglists '([c q])}
  suitable-dispatch-fn)

(defmulti persist!
  {:arglists '([c o])}
  suitable-dispatch-fn)

The implementation:

(ns dbbackend.mysql
  (:requires dbbackend))

(defmethod query com.mysql.jdbc.Connection
  [c q]
  ...)

(defmethod persist! com.mysql.jdbc.Connection
  [c o]
  ...)

The usage:

(ns user
  (:require dbbackend dbbackend.mysql))

(def mysql-connection (connect-to-mysql))
(query mysql-connection some-query)

You can find a real-world example of this approach under the hood of ClojureQL.

kotarak