Mittwoch, 28. Mai 2014

Clojure protocols example 2: Extending existing classes

In my past post "Clojure protocols example" I gave an example about using protocols on new types. But protocols also can be used to extend existing types.

A real world example of this can be found in this library, used to generate message digest (md5, etc) for different input types.

More specifically this file.

Here we see existing data types (Collection, String, File, etc) are extended to implement Digestible protocol. So we can call digest on them - a method which they don't support natively.

This is a different, simplified version, with just what we need to understand:
(defprotocol Digestible
  (digest [digestible]))

(extend-protocol Digestible

  clojure.lang.PersistentVector
  (digest [digestible]
    (str "vector-md5-" digestible))

  clojure.lang.PersistentArrayMap
  (digest [digestible]
    (str "map-md5-" digestible))

  java.lang.String
  (digest [digestible]
    (str "string-md5-" digestible)))


;test
(digest {:a 1 :b 2}) ;map-md5-{:a 1 :b 2} 
(digest [1 2 3]) ;vector-md5-[1 2 3]
(digest "hellooo") ;string-md5-"hellooo"


In this case, we could say, explaining it in object oriented fashion, that we are extending the existing map, vector and string classes in Clojure to support the digest function. A more functional explanation, and thus suitable for this context, would be that we are defining a function, which will execute implementation according to the type of the parameter.
Either way, we call digest with these types, and get the correct hash. In this example, probably needless to say, for demonstrative purposes, we don't return a hash but a text that helps us to see which method was called.

Note that in this example I'm using "digestible" instead of "this" to name the parameter (contrary to the first example I posted about protocols). There's no reason for this, besides I consider "this" maybe to be too engrained into object oriented world and misleading in this context. I may change the other post to use "data-provider" instead of "this". The source of the GitHub project I mentioned uses "message". I consider "digestible" better, since if we pass anything here that's not "digestible", we will get an error. It seems there is no (well known) convention about the naming of this parameter yet, since all the examples I have found use something different.

On a different note, this functionality can also be achieved using multimethods. This is the multimethod version of our example:

(defmulti digest class)

(defmethod digest clojure.lang.PersistentVector [digestible]
  (str "vector-md5-" digestible))

(defmethod digest clojure.lang.PersistentArrayMap [digestible]
  (str "map-md5-" digestible))

(defmethod digest java.lang.String [digestible]
  (str "string-md5-" digestible))

;test
(digest {:a 1 :b 2})
(digest [1 2 3])
(digest "hellooo")


More info on when to use protocol or multimethod e.g. here.

Dienstag, 27. Mai 2014

Clojure protocols example

Task: create a data provider with different implementations e.g. database / memory

;We start with a basic in-memory implementation:
(defprotocol DataProvider
  ;We pass the instance to the functions as "this" (the name is arbitrary)
  (find-user [this id]))

(deftype MemoryDataProvider []
  DataProvider

  (find-user [this id]
    (first (filter #(= (:id %) id)
                   [
                    {:id 1 :name "betty"}
                    {:id 2 :name "mike"}]))))

;Test: create an instance and find user with id 2
(def my-data-provider (MemoryDataProvider.))
(find-user my-data-provider 2)

;In the database implementation we need to pass e.g. host and port
;in order to establish a connection. We do this using the constructor
;function of the type (see DBDataProvider below). 
;We also need to add a function to establish the connection. We have to
;do add it also to the protocol, since the client has to call it and doesn't know 
;the implementation. We add a generic method "init" to the protocol, 
;for this purpose.
;(Note that "init" means side effects, so this doesn't really make
;sense in the in-memory implementation, but don't see a better solution).
(defprotocol DataProvider
  (init [this])
  (find-user [this id]))

;We have to add the method to MemoryDataProvider. Not doing this will
;not cause compiler error, but will throw runtime error if init is
;called on this type
(deftype MemoryDataProvider []
  DataProvider

  (init [this])
  
  (find-user [this id]
    (first (filter #(= (:id %) id)
                   [
                    {:id 1 :name "betty"}
                    {:id 2 :name "mike"}]))))


;Test
(def my-data-provider (MemoryDataProvider.))
(init my-data-provider) ;nil
(find-user my-data-provider 2)


;This is the database data provider type
(deftype DBDataProvider [host port]
  DataProvider

  (init [this]
    (println "host: " host ", port: " port)
    ;connect...
    )
  
  (find-user [this id]
    ;get it from database...
    {:id 0 :name "test"}
    )
  )

(def my-data-provider (DBDataProvider. "127.0.0.1" "1234"))
(init my-data-provider) ;nil
(find-user my-data-provider 2)


;We can also make init return the type:
(deftype DBDataProvider [host port]
  DataProvider

  (init [this]
    (println "host: " host ", port: " port)
    ;connect...
    this
    )
  
  (find-user [this id]
    ;get it from database...
    {:id 0 :name "test"}
    )
  )

;then we can do:
(def my-data-provider (init (DBDataProvider. "127.0.0.1" "1234")))
(find-user my-data-provider 2)


Note: I'm new to Clojure, glad to receive corrections or suggestions in the case something can be improved.
These snippets can be found (together with others) here.

Sonntag, 18. Mai 2014

Practical Clojure partials introduction

Today I found myself practicing a bit the concept of partials in Clojure, and ended writing a tutorial -like script. Here it is, maybe it helps someone else to learn how to use them.

;assume we have a function that does something with a list of numbers
;in this case, it adds a value to each number:
(defn add-value [value numbers]
(map (fn [number] (+ number value)) numbers))

;test
(add-value 2 [1 2 3]) ;(3 4 5)

;now we put this logic in a function "foo":
(defn foo []
  (add-value 2 [1 2 3])

;test
(foo) ;(3 4 5)

;now imagine that besides of add-value we have a mult-value function:
(defn mult-value [value numbers]
  (map (fn [number] (* number value)) numbers))

;or any other function that accept a value and numbers vector as
;parameters, and does something with them!

;we want to customise the
;behaviour - so we pass the behaviour as parameter:
(defn foo [some-function]
  (some-function 2 [1 2 3]))


;now we can call foo with add-value:
(foo add-value) ;(3 4 5)
;or mult-value:
(foo mult-value) ;(2 4 6)


;now imagine a situation where foo passes the vector
;but not the value. Instead, we want to pass the value
;together with some-function

;we could do:
(defn foo [some-function value]
  (some-function value [1 2 3]))

;test
(foo add-value 2) ;(3 4 5)

;this works but it's ugly, and in more complex use cases can quickly
;become a mess - we are altering the signature of foo with
;values which belong rather to some-function.
;so how can we pass the value to some-function instead of passing it
;to foo, despite we don't have all the parameters to call
;some-function yet?
;this is were a partial comes to help

;with a partial we can pass only a part of the parameters to a function,
;letting it "on hold" until the rest is passed.

;we revert to our old signature of foo:
(defn foo [some-function]
  ;note: value is "captured" (see call below) in some-function so we don't pass it here anymore
  (some-function [1 2 3]))

;and we call foo using a partial, which captures the value:
(foo (partial add-value 2)) ;(3 4 5)
(foo (partial mult-value 2)) ;(2 4 6)


;an alternative would be to use partial in the definition of the function
;this would make sense if we are certain that the function is going to be always used as partial:
(defn add-value [value]
  (partial map (fn [number] (+ number value))))

;and call it like this:
(foo (add-value 2)) ;(3 4 5)

;note that, with this implementation, (forgetting about foo for a moment) we can't do this anymore:
(add-value 2 [1 2 3]) ;error
;we would have to use this syntax in order to call add-value will all parameters:
((add-value 2) [1 2 3]) ;(3 4 5)

;if we want to be more flexible, meaning we have the options to use the function
;both as partial or passing all the parameters, we can use multiple arities 
;(like before, it depends on the use case if this makes sense):
(defn add-value
([value] (partial add-value value))
([value numbers]
  (map (fn [number] (+ number value)) numbers)))

;we also use it like this:
(foo (add-value 2)) ;(3 4 5)

;without foo, we can do this, like before:
((add-value 2) [1 2 3]) ;(3 4 5)
;but now we also can do:
(add-value 2 [1 2 3]) ;(3 4 5)


Note: I'm new to Clojure, glad to receive corrections or suggestions in the case something can be improved.
These snippets can be found (together with others) here.