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
,B
andIExample
. And then we have 2 relationships between these things: 1)a-to-example : A -> IExample
2)b-to-a : B -> A
and 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 protocolIToExample
like shown below:What we did that we represented the
-> IExample
in our design as theIToExample
protocol with one function. So we got:a-to-example : A -> IExample
by implementing IToExample for Ab-to-a : B -> A
by a normal functioncompose b-to-a with a-to-example : B -> IExample
by implementing IToExample for B and using b-to-a.