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