(defprotocol Addable
(add-fields [this]))
(defrecord MyRecord [a b]
Addable
(add-fields [this] (+ a b)))
;;; Magic happens here
(defn indirect-adder [a b]
(add-fields (MyRecord. a b)))
(with-definition-of-add-fields-changed-to (fn [_] "hi mom")
(indirect-adder 1 2)
=> "hi mom" ; rather than 3
I expect there are no tricks like :dynamic true <http://blog.n01se.net/?p=134> that work, but I thought I'd check.
-----
Brian Marick, Artisanal Labrador
Contract programming in Ruby and Clojure
Occasional consulting on Agile
www.exampler.com, www.twitter.com/marick
> The protocol method `add-fields` becomes an ordinary Clojure Var. You can temporarily change its root binding using `with-redefs` in 1.3.
`with-redefs` has no effect on an already-compiled function that uses a defrecord-defined function. I would need something that says "O compiler, being able to redef functions is actually more important to me right now than efficiency, so please emit code that notices redefs in, for example:
(defn indirect-adder [a b]
(add-fields (MyRecord. a b)))"
I have a kinda-kludgy workaround that does essentially that. It suffices.
> Recently the received wisdom has been: protocols are a low-level implementation detail. Actual APIs should be built with normal functions that call the protocol methods.
Interesting. So I'm not encouraged to think "protocols" except when I care about efficiency or Java interop?
I don't think so. I see it more that the design of your lib should not
be centered around expecting the user to extend and call protocol
functions all over the place.
But rather provide classic hof leveraging internally polymorphic parts
via protocols, and exposing those hof as APIs.
If required, the user may extend some of your librarie's protocols to
his own types or to types he doesn't own (at the risk of shadowing
your librarie's own extension).
But still, the functions of the protocol(s) the user will extend will
not be those which will be called in his code.
HTH,
--
Laurent
Am 01.07.2011 um 00:16 schrieb Laurent PETIT:
>> On Jun 30, 2011, at 7:54 AM, Stuart Sierra wrote:
>>
>>> Recently the received wisdom has been: protocols are a low-level implementation detail. Actual APIs should be built with normal functions that call the protocol methods.
>
> But still, the functions of the protocol(s) the user will extend will
> not be those which will be called in his code.
I don't buy that. Why should protocol functions not be part of the public API?
(defprotocol Associative
(get [this k]))
(extend-protocol Associative
Vector
(get [this idx] ...)
Map
(get [this k] ...))
Looks perfectly fine to me. In fact: how do you want to hide the protocol here? I think the point is that the protocol does not have to comprise the whole API. It can be perfectly complemented by normal functions. Or it could be just "internal" in terms of the user side being only normal functions (the scenario you describe). But I think there are relatively simple use cases (see above), where it doesn't have to be that way.
Sincerely
Meikel
But, you'd never do that, right? Each implementation of Vector and Map would have their own idiosyncratic implementations of `get`, and you _want_ (need) that bolted into the deftyped class.
Brian was looking to be able to dynamically rebind a protocol fn, which is simply impossible for classes that have inline implementations. The solution in such cases seems to be that such protocol fns should be implemented via extend, which would then allow for any kind of rebinding or HOF action you want.
Unless I'm missing something, I'd revise the "received wisdom" to say "inlined protocol methods are a low-level implementation detail". If you want the perf, go for it, but know that you're giving up some of the benefits of vars for instances of classes that have direct impls. That sounds like a perfectly reasonable tradeoff.
- Chas
To be honest, i was just trying to "expand on my own current
understanding of the 'received wisdom'", 'cause I also (as Brian did)
found that this 'wisdom' is currently poorly explained, letting each
and every person "extend it to its own fully qualified demonstration".
By being explicit about what I have inferred from the condensed bits
that are generally served, my intention is to be proven wrong or not,
but by means of arguments and more details.
And I'm not sure that the 'received wisdom' is just about "inlining
protocol methods" vs other more flexible (but less performant)
techniques of extending protocols ...
But, you'd never do that, right? Each implementation of Vector and Map would have their own idiosyncratic implementations of `get`, and you _want_ (need) that bolted into the deftyped class.Brian was looking to be able to dynamically rebind a protocol fn, which is simply impossible for classes that have inline implementations. The solution in such cases seems to be that such protocol fns should be implemented via extend, which would then allow for any kind of rebinding or HOF action you want.
Unless I'm missing something, I'd revise the "received wisdom" to say "inlined protocol methods are a low-level implementation detail". If you want the perf, go for it, but know that you're giving up some of the benefits of vars for instances of classes that have direct impls. That sounds like a perfectly reasonable tradeoff.
I think a great example of this is clojure.java.io. The api is
"reader" and "writer", but the implementation is in the Coercions and
IOFactory protocols.
It's the largest "real world" usage of protocols I've seen so far, and
it's quite a nice, I think.
--Aaron
Am 02.07.2011 um 22:07 schrieb Christophe Grand:
> my 2 cents,
Understatement!
Thank you for the clarifications. I wrote them up with an example (hopefully) illustrating the issue. http://bit.ly/nOHgle
Cordialement
Meikel