In a project using clojure.java.jmx, I was extending it's Destract protocols objects->data function to transform more of the JMX data structures returned from calls or metadata queries into plain clojure data structures.
When I was done with the individual data structures, it should have been possible to do a (walk/prewalk jmx/objects->data (jmx/operations "java.lang:type=Threading")).
However, in the Destract protocol there's an implementation of the objects->data function for the type clojure.lang.Associative that would mean maps would be processed incorrectly.
I could add an implementation for clojure.lang.IPersistentMap in my namespace, but since clojure.lang.Associative is also an interface for maps this would not work.
I ended up having to fork clojure.java.jmx because of it. If there would be a way to change preference, or retract a protocol for a type within another namespace I wouldn't have had to.
Is there a way to prevent clojure.lang.Associative taking precedence over clojure.lang.IPersistentMap in the protocol ?
If there isn't, is it possible to retract a protocol for a type in another namespace ? Would it even be possible to implement it in regard to the way protocols are compiled into Java interfaces ?
It should work
Are you sure that providing your own implementation for clojure.lang.IPersistentMap won't work? It works in my REPL session. It even works when I just override the default implementation for clojure.lang.Associative:
user=> (ns ns1)
;;=> nil
ns1=> (defprotocol IPrintable (prnt [this]))
;;=> IPrintable
ns1=> (extend-protocol IPrintable clojure.lang.Associative (prnt [this] (str "clojure.lang.Associative " this)))
;;=> nil
ns1=> (ns ns2)
;;=> nil
ns2=> (ns1/prnt {:a 1})
;;=> "clojure.lang.Associative {:a 1}"
ns2=> (extend-protocol ns1/IPrintable clojure.lang.Associative (prnt [this] (str "My custom impl for clojure.lang.Associative " this)))
;;=> nil
ns2=> (ns1/prnt {:a 1})
;;=> "My custom impl for clojure.lang.Associative {:a 1}"
It also works in a sample project.
You must remember to require the namespace where you extend-protocol if it's not loaded transitively by another namespace. It's important that your extend-protocol will be loaded after the one you would like to override.
Internally extend-protocol or extend-type modify a special map attached to protocol's metadata where it assocs a given type with a function implementation. If there was an existing entry for a given type it will be overridden. Thus the order in which extend-* are executed is important.
When it won't work
When an object implements an interface or a protocol directly (e.g. inline in defrecord) you cannot override the implementation (continued REPL session):
ns2=> (defrecord SomeData []
ns1/IPrintable (prnt [this] "I'm SomeData"))
;;=> ns2.SomeData
ns2=> (ns1/prnt (SomeData.))
;;=> "I'm SomeData"
ns2=> (extend-protocol ns1/IPrintable SomeData (prnt [this] "Custom impl " this))
;;=> IllegalArgumentException class ns2.SomeData already directly implements interface ns1.IPrintable for protocol:#'ns1/IPrintable clojure.core/extend (core_deftype.clj:775)
Related
How to mock a specific implementation of a protocol, preferably with plain Clojure i.e. no external libs e.g. Midje? I know how to mock all implementations but not specific ones:
(defprotocol P
(foo [p]))
(defrecord R1 [])
(defrecord R2 [])
(extend-protocol P
R1
(foo [_] :r1)
R2
(foo [_] :r2))
(foo (->R1)) ;; :r1
(foo (->R2)) ;; :r2
;; now the mocking ...
(with-redefs [foo (constantly :redefed)]
(println (foo (->R1))) ;; :redefed
(println (foo (->R2)))) ;; :redefed
i.e. how to get (foo (->R1)) to return :redefed while (foo (->R2)) still returns :r2?
Assume I can only make changes below the now the mocking ... comment. Notice that I am putting aside the recommendation to extend a protocol only if I have control of the protocol, the type, or both.
You could create a new protocol containing methods with the same signature and use that when rebinding:
(def foo-orig foo)
(defprotocol Q
(bar [q]))
(extend-protocol Q
R1
(bar [_] :redefed)
R2
(bar [this] (foo-orig this)))
Note you'll have to capture the original foo definition for the implementations you don't want to change. Then:
(with-redefs [foo bar]
(println (foo (->R1))) ;; :redefed
(println (foo (->R2)))) ;; :r2
or you could defined a multimethod e.g.
(defmulti bar (fn [q] (type q)))
(defmethod bar R1 [_]
:redefed)
(defmethod bar :default [q]
(foo-orig q))
My first thought is to have the foo impls delegate to a helper fn:
(extend-protocol P
R1
(foo [p] (foo-r1 p))
R2
(foo [p] (foo-r2 p)))
with
(defn foo-r1 [_] :r1)
(defn foo-r2 [_] :r2)
and then just redef foo-r1 and foo-r2 independently as desired.
Note also that with-redefs is meant to operate on var instances, while the foo you define as part of a protocol is not the same as a var. That may be the cause of your global redefinition problem.
Update
You may need to clarify your use-case & update the question. See this part at clojure.org:
Extend only things you control You should extend a protocol to a type
only if you control the type, the protocol, or both. This is
particularly important for the protocols included with Clojure itself.
It might be easiest to define wrapper functions for protocol internal implementation functions and call them from outside.
Then it can be mocked easily with with-redefs et al.
It also has other benefits, e.g. being able to define specs for such functions.
Don't pass foo an R1 at all. Define a new implementation, R1Mocked, that does whatever you want, and pass that instead. This is exactly the sort of polymorphism that protocols are designed to support.
My goal is a function/macro that works like this:
(def f (polymorphic-fn
java.lang.Long (fn [a] (inc a))
java.lang.String (fn [a] (str a "x"))))
(f 1) ;; => 2
(f "abc") ;; => "abcx"
Since the type-based dispatch of protocols has the best performance I was thinking to create an 'anonymous' protocol for the 'fused' function with a macro like this:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym)
method (gensym)
extends (for [[type handler] pairs]
`(extend ~type ~proto {(keyword ~method) ~handler}))]
`(do
(defprotocol ~proto (~method [e#]))
~#extends
~method)))
This produces the error: Unable to resolve symbol: G__18707.
Is there a way to return the 'anonymous' method, or is there a better way to implement such a function?
The problem is that the defprotocol will generate code that will intern the protocol methods. Ie. after macroexpansion, the symbols for your defined method is still not known to the compiler. Thus, the compilation fails and will barf that the symbol is unknown.
Many other def...'s will generate a macro "call" that will intern the symbol during macro expansion (and thus the compiler will remain happy).
To fix it you can just declare it beforehand. This works since declare is macro, will get expanded and the compiler will be happy:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym "proto")
method (gensym "prot-method-")
extends (for [[type handler] (partition 2 pairs)]
`(extend ~type ~proto {~(keyword (str method)) ~handler}))]
`(do
(declare ~method)
(defprotocol ~proto (~method [e#]))
~#extends
~method)))
Note: I also fixed the keyword call in this.
I think you just want to use regular protocols, along with extend-type:
(defprotocol Fooable
(foo [this]) )
(extend-type java.lang.Long
Fooable
(foo [some-long] (inc some-long)))
(extend-type java.lang.String
Fooable
(foo [any-string] (str any-string "-and-more")))
with result:
(foo 3) => 4
(foo "hello") => "hello-and-more"
It may be possible to use a macro to hide the protocol name by using an auto-gensym, but I don't see the point. Just ignore the protocol name 'Fooable' and you have the same result.
Also, be aware that parts of Clojure implementation create concrete Java classes behind the scenes, which may require a hard-coded name.
You could also mimic the protocol functionality by using a cond:
(defn bar [it]
(cond
(= (class it) java.lang.Long) (inc it)
(= (class it) java.lang.String) (str it "-and-more")))
(bar 7) => 8
(bar "farewell") => "farewell-and-more"
You could define a function to generate bar like you do with polymorphic-fn if you wanted.
I have been reading the 'Clojure Rationale' here:
http://clojure.org/rationale
In the Polymorphism section it reads:
Clojure multimethods decouple polymorphism from OO and types
Supports multiple taxonomies
Dispatches via static, dynamic or external properties, metadata, etc
What is meant by 'supports multiple taxonomies' here? I fact, what is a taxonomy in this case? Thanks
user> (defmulti shade :color)
nil
user> (defmethod shade :black [_] "darkest of darks")
#<MultiFn clojure.lang.MultiFn#22b90f93>
user> (defmethod shade :white [_] "all frequencies are reflected")
#<MultiFn clojure.lang.MultiFn#22b90f93>
user> (defmulti movement :legs)
#'user/movement
user> (defmethod movement 2 [_] "walks on hind legs")
#<MultiFn clojure.lang.MultiFn#13b58075>
user> (defmethod movement 4 [_] "proceeds on all fours")
#<MultiFn clojure.lang.MultiFn#13b58075>
user> ((juxt movement shade) {:name "cat" :legs 4 :color :black})
["proceeds on all fours" "darkest of darks"]
In the above code, two systems of organization are created - one in terms of color, the other in terms of legs. Both of these taxonomies are equally valid, and the same object can fall into different places depending on the taxonomy used.
Multimethods can also use hierarchies for dispatch (see derive and related functions), where each hierarchy can co-exist in parallel (unlike the unified view of Class hierarchies).
user> (derive ::manx ::cat)
nil
user> (defmulti favorite-treat :species)
#'user/favorite-treat
user> (defmethod favorite-treat ::cat [_] "Tuna")
#<MultiFn clojure.lang.MultiFn#264d27e6>
user> (derive ::indoor ::domestic)
nil
user> (defmulti activity :tameness)
#'user/activity
user> (defmethod activity ::domestic [_] "window watching")
#<MultiFn clojure.lang.MultiFn#1654bf3f>
user> ((juxt favorite-treat activity) {:species ::manx :tameness ::indoor})
["Tuna" "window watching"]
here the same kitty is a member of two hierarchies - one of domestication, and the other of genetics, and is can have its methods resolved by either as appropriate.
Also, even setting asid multimethods, relationships created via derive support multiple inheritance, unlike the jvm Class heirarchy Clojure is built on:
user> (derive ::foo ::bar)
nil
user> (derive ::foo ::baz)
nil
user> (derive ::quux ::foo)
nil
user> (isa? ::quux ::foo)
true
user> (isa? ::quux ::bar)
true
user> (isa? ::quux ::baz)
true
user> (isa? ::bar ::baz)
false
Clojure, like many other functional languages, takes advantage of loose typing to provide easy ways to implement parametric polymorphism. This basically means that a single method can be constructed in a way that it does not care about the types of values the arguments it is given are.
Take for example the concat method. It takes any number of arguments of varying forms and puts them into a single list like so:
user=> (concat [:a :b] nil [1 [2 3] 4])
(:a :b 1 [2 3] 4)
Because one does not need to declare a typing for arguments, concat can be written in such a way that you can provide an argument of any type (vector, function, keyword, string, etc.) and it will act upon them in a similar way.
Clojure's multimethods allow you to support this concept on Java objects that may have completely different structures (i.e. taxonomies) by using metadata or other properties to determine the appropriate way to deal with the given argument. See the following example (taken from defmulti):
(defmulti greeting
(fn[x] (x "language")))
(defmethod greeting "English" [params]
(str "Hello! " (params "id")))
(defmethod greeting "French" [params]
(str "Bonjour! " (params "id")))
=>(greeting {"id" "1", "language" "English"})
"Hello! 1"
=>(greeting {"id" "2", "language" "French"})
"Bounjour! 2"
The greeting method returns the "language" value from the map which matches the "English" or "French" defmethod which returns the correct corresponding value. Hopefully, you can see how this concept could potentially be applied to just about any kind of data or object structure. This powerful idea of polymorphism is what the Clojure developers are trying to show off in the rationale page.
let's try some calls to the "type" function :
user=> (type 10)
java.lang.Integer
user=> (type 10.0)
java.lang.Double
user=> (type :keyword?)
clojure.lang.Keyword
and now with an anonymous function :
user=> (type #(str "wonder" "what" "this" "is"))
user$eval7$fn__8
A) what does this mean "user$eval7$fn__8" ?
B) and what type is a function ?
the source for "type" is :
user=> (source type)
(defn type
"Returns the :type metadata of x, or its Class if none"
{:added "1.0"}
[x]
(or (:type (meta x)) (class x)))
nil
so a function needs to have a specific part of meta data or be a class
checking the meta of an anonymous function yields nada :
user=> (meta #(str "wonder" "what" "this" "is"))
nil
trying a different approach :
user=> (defn woot [] (str "wonder" "what" "this" "is"))
#'user/woot
user=> (meta woot)
{:ns #<Namespace user>, :name woot}
C) seems there is some meta but i figured this is the meta of the symbol "woot", right ?
what about the second half of the "or" :
user=> (class #(str "wonder" "what" "this" "is"))
user$eval31$fn__32
user=> (class woot)
user$woot
what are these : "user$eval31$fn__32" and "user$woot" and where do they come from ?
checking out the "class" function yields:
user=> (source class)
(defn ^Class class
"Returns the Class of x"
{:added "1.0"}
[^Object x] (if (nil? x) x (. x (getClass))))
nil
and further investigating yields :
user=> (.getClass #(str "wonder" "what" "this" "is"))
user$eval38$fn__39
user=> (.getClass woot)
user$woot
i don't get it.
D) is this a hashcode : eval38$fn__39 ?
E) is this a symbol : woot ?
F) why doesn't a function have a type ? isn't it supposed to be an IFn or something ?
A function is of type clojure.lang.IFn, which is a Java interface.
Every Clojure function is compiled into a Java class which implements clojure.lang.IFn. The name user$eval7$fn__8 is the "binary class name" of that class, i.e., its internal name in the JVM.
Clojure is built on the JVM.
The JVM doesn't support first-class functions, or lambdas, out of the box. Every Clojure function, once it is compiled, becomes its own anonymous class from the perspective of the JVM. Each function is, technically, it's own type.
The class it becomes implements IFn, but when you retrieve it's type, it gives you the name of the anonymous class which is different every time.
Here's the description in the API docs
Returns the :type metadata of x, or its Class if none
What you are seeing ("user$eval7$fn__8") is the name of the internally generated inner class created by Clojure to implement the anonymous function you have defined.
As you may have noticed, Clojure doesn't follow standard Java class naming conventions :-)
Note that the class implements the interface clojure.lang.IFn - this applies to all Clojure functions.
I am a clojure novice but i am going to be bold. First we have two different meaning for "type" of a function, one, the java interfaces and classes of clojure internals and otoh the type of the function as a programming concept. Taking the second approach the type of a function would be the type of its return value (or its params types and return value type):
1) i guess all functions implements the IFn interface, whatever their current class
2) the class name automatic generated by clojure diffs if the function is anonymous or named, but it seems in both cases are inner classes (tipically theirs names are separated by $ and go from outer classes to inner ones)
3) the type of the returned value can be in the :tag key of function metadata if you annotate it in function definition. F.e. the function class you expose have Class as its returned type cause in its def there is a ^Class before the name.
I am assuming you are familiar with java (or a similar oop lang), sorry if not
Is there some way to see what has already been defined in a clojure session (equivalent to calling ls())? Let's say that I create a few objects:
(def x 1)
(def y 2.2)
(def plus-one (fn [x] (+ x 1)))
Is there a command that can be run to show me that these now exist in the user namespace?
I am doing all the assignments in user namespace.
user> (def *foo 10)
#'user/*foo
;; To see all the public intern mappings in the user namespace.
user> (ns-publics 'user)
{*foo #'user/*foo}
Now let's define a function which is not public
user> (defn- foobar[x]
(println x)
#'user/foobar
When you call ns-publics function. It will not show foobar function in the mappings.
user> (ns-publics 'user)
{*foo #'user/*foo}
To see the intern mappings for the namespace. Use (ns-interns 'YOUR-NAMESPACE)
user> (ns-interns 'user)
{foobar #'user/foobar, *foo #'user/*foo}
Maybe ns-publics ?
Returns a map of the public intern mappings for the namespace.
or ns-map ?
Returns a map of all the mappings for the namespace.
As I understand it, there is no "global environment," there are only namespaces. Of course whichever one you are currently "in" looks like a "global environment" for practical purposes.