I have a protocol called IExample and I define a record type A that implements it:
(defprotocol IExample
(foo [this] "do something")
(bar [this] "do something else"))
(defrecord A [field1 field2]
IExample
(foo [this]
(+ field1 field2))
(bar [this]
(- field1 field2)))
Let's say I want to extend another (basic) type B to implement this protocol, but I know how to convert from B to A:
(defn B-to-A
"converts a B object to an A object"
[Bobj] ...)
because I have this conversion, I can delegate all calls of the IExample protocol
on a B to the IExample protocol on an A by delegating them:
(extend B
IExample {
:foo (fn [this] (foo (B-to-A this)))
:bar (fn [this] (bar (B-to-A this)))})
This, however, seems as an awful lot of boilerplate (especially for bigger protocols) that is not clojure-idiomatic.
How can I tell clojure just to implicitly convert B to A every time
an IExample function is called on a B object, using the B-to-A function?
As far as the boilerplate is concerned, you can write some macro to write all that boilerplate for you. On the other hand, you could have a second look at your design here.
What we have here is 3 things (types):
A,BandIExample. And then we have 2 relationships between these things: 1)a-to-example : A -> IExample2)b-to-a : B -> Aand from this we can get 3rd relationship by using composition i.ecompose b-to-a with a-to-example : B -> IExample. Now if we try to move this design to protocols we will find that it is not a simple translation because protocols won't directly compose as discussed in the above design, instead we can use an intermediate protocolIToExamplelike shown below:What we did that we represented the
-> IExamplein our design as theIToExampleprotocol with one function. So we got:a-to-example : A -> IExampleby implementing IToExample for Ab-to-a : B -> Aby a normal functioncompose b-to-a with a-to-example : B -> IExampleby implementing IToExample for B and using b-to-a.