Hi !

For clojure, a type or a record, represent data. A protocol represent
a set of linked polymorph functions. But protocol themselves are not
types. Just grouped functions. The types is what they apply on. Not
what they are themselves.

You can view each method of a protocol like a big swich function that
test the type of it first argument and choose the right implementation
of the function depending of the type of this first argument.

Type can be extended to support a protocol. By doing that you define
the call that will be called when you call the protocol function with
a first parameter of the extended type. Type is a record or a type.
For java interop support, it can be also any java class/interface.

Clojure intentionnaly avoid support for inheritence. A record/type
can't inherit from another record/type. And protocol can't be extended
to another protocol. Because in clojure this would made no sence. A
function can't inherit from another function.

This is by design. Clojure avoid to support inheritence for clojure
concepts. It only support for java interop because this might be
needed for interfacing with an existing java library (Like swing
listeners).

In your example, extending cascade.Asset work because it is considered
as a java interface, and is supported for interoperability. But this
is likely not what you really want to do as Asset is still not a
clojure type.

Hope this help,

Nicolas

On 28 oct, 19:46, Howard Lewis Ship <hls...@gmail.com> wrote:
> I'm hitting a bit of frustration with (defprotocol) and friends and
> I'm hoping someone can enlighten me:
>
> Context: this is part of the Cascade layer I'm building on top of
> Compojure. It's all about rendering markup, borrowing ideas from
> Tapestry.
>
> I have a protocol that represents Assets: any kind of static resource
> that can be exposed to the client via a URL, such as a JavaScript
> library or stylesheet:
>
> (defprotocol Asset
>   "Represent a server-side resource so that it can be exposed
> efficiently to the client."
>   (^InputStream content [asset] "Returns the content of the Asset as a
> stream of bytes, or null if the Asset does not exist.")
>   (^String client-url [asset] "Returns an absolute URL to the Asset."))
>
> Cascade builds a DOM tree before streaming; one step of that is to
> convert attribute values inside DOM nodes into strings.
>
> (defprotocol ToAttributeValueString
>   "Converts an attribute value to a string. It is not necessary to
> apply quotes (those come at a later stage)."
>   (to-attribute-value-string [value]
>     "Converts the value to a string that can be safely streamed as an
> attribute value."))
>
> (extend-protocol ToAttributeValueString
>   String
>   (to-attribute-value-string [string] (encode-string string))
>
>   Number
>   (to-attribute-value-string [num] (.toString num))
>
>   Keyword
>   (to-attribute-value-string [kw] (encode-string (name kw))))
>
> Now, I want Assets to be used as attribute values, so:
>
> (extend-type Asset
>   ToAttributeValueString
>   (to-attribute-value-string [asset] (:client-url asset)))
>
> But this fails with:
>
> Exception in thread "main" java.lang.IllegalArgumentException: Unable
> to resolve classname: Asset, compiling:(cascade.clj:84)
>         at clojure.lang.Compiler.analyzeSeq(Compiler.java:6416)
>         at clojure.lang.Compiler.analyze(Compiler.java:6216)
>         at clojure.lang.Compiler.analyzeSeq(Compiler.java:6397)
>         at clojure.lang.Compiler.analyze(Compiler.java:6216)
>         at clojure.lang.Compiler.analyze(Compiler.java:6177)
>         at clojure.lang.Compiler$MapExpr.parse(Compiler.java:2782)
>         at clojure.lang.Compiler.analyze(Compiler.java:6224)
>         at clojure.lang.Compiler.analyze(Compiler.java:6177)
>         at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3503)
>         at clojure.lang.Compiler.analyzeSeq(Compiler.java:6411)
>         at clojure.lang.Compiler.analyze(Compiler.java:6216)
>         at clojure.lang.Compiler.analyze(Compiler.java:6177)
>         at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5572)
>         at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5008)
>         at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3629)
>         at clojure.lang.Compiler.analyzeSeq(Compiler.java:6407)
>         at clojure.lang.Compiler.analyze(Compiler.java:6216)
>         at clojure.lang.Compiler.eval(Compiler.java:6462)
>         at clojure.lang.Compiler.load(Compiler.java:6902)
>         at clojure.lang.RT.loadResourceScript(RT.java:357)
>         at clojure.lang.RT.loadResourceScript(RT.java:348)
>         at clojure.lang.RT.load(RT.java:427)
>         at clojure.lang.RT.load(RT.java:398)
>         at clojure.core$load$fn__4610.invoke(core.clj:5386)
>         at clojure.core$load.doInvoke(core.clj:5385)
>         at clojure.lang.RestFn.invoke(RestFn.java:408)
>         at clojure.core$load_one.invoke(core.clj:5200)
>         at clojure.core$load_lib.doInvoke(core.clj:5237)
>         at clojure.lang.RestFn.applyTo(RestFn.java:142)
>         at clojure.core$apply.invoke(core.clj:602)
>         at clojure.core$load_libs.doInvoke(core.clj:5271)
>         at clojure.lang.RestFn.applyTo(RestFn.java:137)
>         at clojure.core$apply.invoke(core.clj:604)
>         at clojure.core$use.doInvoke(core.clj:5363)
>         at clojure.lang.RestFn.invoke(RestFn.java:436)
>         at main$eval3$loading__4505__auto____4.invoke(main.clj:1)
>         at main$eval3.invoke(main.clj:1)
>         at clojure.lang.Compiler.eval(Compiler.java:6465)
>         at clojure.lang.Compiler.eval(Compiler.java:6455)
>         at clojure.lang.Compiler.load(Compiler.java:6902)
>         at clojure.lang.Compiler.loadFile(Compiler.java:6863)
>         at clojure.main$load_script.invoke(main.clj:282)
>         at clojure.main$script_opt.invoke(main.clj:342)
>         at clojure.main$main.doInvoke(main.clj:426)
>         at clojure.lang.RestFn.invoke(RestFn.java:408)
>         at clojure.lang.Var.invoke(Var.java:401)
>         at clojure.lang.AFn.applyToHelper(AFn.java:161)
>         at clojure.lang.Var.applyTo(Var.java:518)
>         at clojure.main.main(main.java:37)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>         at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>         at 
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>         at java.lang.reflect.Method.invoke(Method.java:597)
>         at 
> com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
> Caused by: java.lang.IllegalArgumentException: Unable to resolve
> classname: Asset
>         at clojure.lang.Compiler$HostExpr.tagToClass(Compiler.java:1003)
>         at clojure.lang.Compiler.tagClass(Compiler.java:7878)
>         at clojure.lang.Compiler$FnMethod.parse(Compiler.java:4955)
>         at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3629)
>         at clojure.lang.Compiler.analyzeSeq(Compiler.java:6407)
>         ... 53 more
>
> What I eventually discovered was I had to qualify it as:
>
> (extend-type cascade.Asset
>   ToAttributeValueString
>   (to-attribute-value-string [asset] (:client-url asset)))
>
> From my perspective, defprotocol appears to create a name (in the
> current namespace) as well as a Java interface (the real type). It
> feels to me like I should be able to pass either the interface or the
> protocol into extend-type and have it Just Work. Is there some concern
> I'm missing here? Thoughts?
>
> --
> Howard M. Lewis Ship
>
> Creator of Apache Tapestry
>
> The source for Tapestry training, mentoring and support. Contact me to
> learn how I can get you up and productive in Tapestry fast!
>
> (971) 678-5210http://howardlewisship.com

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to