--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
Take your example of a function that has a reasonable default for any object. Often such a function does not need to be part of the contract for implementers (i.e. the protocol) at all.
To make this more concrete, look at how protocols are used in Clojure itself. InternalReduce is a contract for implementers, consumers call reduce. Ditto for IOFactory: clients don't call it, they call the functions reader, writer, etc. instead.
Stu
Stu, (or anybody) I'd like to ask about a variation on this point. How
do you handle the case where you have a general function that works
for every type you'd like to implement a protocol for (thus not
technically needing to be in a protocol), but maybe 1 or 2 of the many
types have more efficient implementations possible? Do you just suck
it up and copy and paste the general function around? Or is there a
better way? Maybe the new case function?
After reading the docs on clojure.org/protocols, it seemed like you
could just define the general implementation of functions under Object
and then define your specific implementations on the other types, but
this didn't seem to work for me. I would get an error to the effect of
"foo is not defined for Type"
The approach of extending with maps is great but lacks of access to
the content of the instance variables of the type.
(def Foo [bar]
)
(extend
clever-automatic-construction
)
As far as I know, clever-automatic-construction cannot use bar in its
implementation.
Which might be a bit annoying, because you have to rely on the
interface of the implementor of one thing
to implement another interface.
Maybe creating a getBar automatically could help?
Or there are other ways to work around that?
Best,
Nicolas.
It could also be helpful to have an easy access (meta information, for
example) to the
fields (and their types) of a record type.
I just discovered (.bar (Foo. 5)) => 5
It is a bit embarrassing. Sorry about that. (I am not a Java person, I reckon)
As far as traits are concerned, I think they can be made with a couple
of macros.
I might try to have a go over the week-end.
Best,
Nicolas.
> I find that I'm horribly confused at this point about what a
> protocol "is". Can someone use some other comp. sci. terms to
> define this idea? I thought of them as Java interfaces with
> default methods but clearly I'm wrong.
>
Coming from CL, the best analogy is that a protocol is a *named* set
of generic functions that dispatch on the type of their first
argument. When you define a protocol with foo/bar/baz methods you get
generic functions in the protocol's namespace named foo/bar/baz.
Unlike interfaces, protocols require no derivation. You can extend a
protocol to a particular type by supplying a concrete map of method
names to function objects (extend), *or* the code for same (extend-
type), *or* by supplying definitions for the protocol methods inline
in a deftype/defrecord. The latter *looks* a lot like implementing an
interface, by design it is as easy to use, and might actually involve
implementing an interface under the hood (but that is an
implementation detail, enabled by the naming and first-arg dispatch
restriction).
Finally, from Java-land, you can get your type to extend a protocol by
deriving from the protocol's corresponding interface. This is what
seems to confuse people most, as if protocols are magic dynamic
interfaces. They are not. They *sometimes* use interfaces in the
implementation, but not always. At no point should one assume that
because a type supports a protocol it 'isA' that protocol (or its
interface).
Protocols are not about inheritance and that is the key distinction vs
interfaces.
Hope that helps,
Rich
That's very interesting. From a performance point of view, is there a
penalty involved in being in a case
where the implementation does not use interfaces?
Interesting...
Is there a link explaining how it is implemented in the extend
situation and how much slower it is?
Cheers,
Nicolas
You can just extend PrettyPrintable to Object.
Stu
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?
No, you have this backwards. The protocol is not on Object, Object is on the protocol. Protocols live in namespaces. You can have 10,000 different protocols extended to Object with no bloat problems. You can have multiple methods with the same names but different semantics, and use any or all of them as you see fit.
This is a major benefit of decoupling polymorphism from inheritance. There is no bloat, and no sacred place (on Object) where you compete for names.
Stu
I called it type classes because it does a part of what Haskell's type
classes do.
You can define a rule:
Node => PrettyPrintableNode (implementation of PrettyPrintableNode using Node)
And it extends Object with a default implementatiuon of protocol
PrettyPrintable, that just takes the object it is called on,
looks at its type, and try to apply the rules it can apply to it (You
could have a rule NodeV2-25 => PrettyFormatable and another
PrettyFormatable, ... => PrettyPrintable).
If it finds a non-cyclic path to construct a PrettyPrintable instance,
it extend the types with the right protocols implementation, and
recalls the function.
I planned to improve on it, but it seems there were a really low
interest for this kind of thing on the list when I posted.
I wouldn't recommand to use it in this state, but if you want to have
a look at the code:
https://nicolasoury.repositoryhosting.com/trac/nicolasoury_type-classes
Best,
Nicolas.
> ;; A node has a data attachment and (possibly) children
> (defprotocol Node
> (data [n])
> (children [n]))
>
> (deftype SimpleNode [d]
> Node
> (data [n] d)
> (children [n] []))
>
> ;; In version 2, I add want to add pretty-printing
>
> (defprotocol PrettyPrintableNode
> (pretty-print [n]))
>
> ;; Make anything potentially pretty-print'able
> (extend-type Object
> PrettyPrintableNode
> (pretty-print [n] (str "A node: " (data n))))
I'm confused by the mixture of formality and looseness here.
When you extend this way, binding this `pretty-print' implementation to
arguments of type Object, you're implicitly requiring that argument "n"
satisfies the protocol Node -- because you call the `data' function on
it. But if the function `data' would work on any kind of Object, then so
too would any call to `pretty-print', right? Should the call to `data'
within the `pretty-print' definition above require some mention of
protocol Node?
In other words, what's the difference between using `extend-type' here
on Object and just writing
,----
| (defn pretty-print
| [n]
| (str "A node: " (data n)))
`----
My experiments in the REPL don't show any difference in behavior.
Being able to define a normal `pretty-print' function above differs from
the generic function system in Common Lisp. There, if one has defined a
generic function with `defgeneric', and one later tries to define a
normal function with the same name, the normal function replaces the
generic function, and the compiler may emit a warning. Working the other
way, though, one may not define a generic function -- using `defgeneric'
or `defmethod' -- if the function name is already bound to a normal
function, macro, or special operator.
Is there supposed to be a difference between the normal function
`pretty-print' I wrote above and the function defined in the
`extend-type' form quoted above?
--
Steven E. Harris
isa? should be satisfies?, I think.