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-5210 http://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