Reputation: 18005
In the e-book about specter, there is this passage:
We need to define ALL’s type like this:
(deftype AllType [])
Finally, we need to add the AllType implementation of select*
(extend-type AllType Navigator (select* [this structure continuation] ...))
(If you’re fluent with protocols, you may wonder why we don’t define select* when we define the type:
(deftype AllType [] Navigator (select* [this structure continuation] ...))
The reason is that the implementation-dependent lookup of specific protocol functions doesn’t work for functions defined that way.)
I don't really get what the author is trying to say here. What is the difference between extend-type
and deftype
+ in-place implementation?
Upvotes: 1
Views: 1304
Reputation: 29958
I just finished reading the Clojure Polymorphism book which discusses this a bit in the last chapter. You may wish to look there for more details.
The gist of it is that they seem the same conceptually, but they are not. He describes a semantic "gotcha" since a Java class also defines a namespace. For example:
(ns my.ns)
(defprotocol Foo (run [this]))
(ns your.ns)
(defprotocol Foo (run [this]))
(ns user)
(defrecord FooAble [])
(extend-protocol my.ns/Foo
FooAble
(run [this] (println "Foo Me")))
(extend-protocol your.ns/Foo
FooAble
(run [this] (println "Foo You")))
(println 1 (->FooAble))
(def some-foo (->FooAble))
(my.ns/run some-foo) => Foo Me
(your.ns/run some-foo) => Foo You
In Java you would try to say
someFoo.run() => Which `run` method...?
So in Clojure I can have 2 run
methods with the same name & signature on a single object iff I use extend-protocol
. If I try to define inline it crashes:
(defrecord BeerAble []
my.ns/Beer
(run [this] (println "Beer Me"))
your.ns/Beer
(run [this] (println "Beer You")))
(println 2 (->BeerAble))
(def some-beer (->BeerAble))
(my.ns/run some-beer)
(your.ns/run some-beer)
;=> CompilerException java.lang.ClassFormatError: Duplicate method name&signature in class file user/BeerAble,
So the inline definnition is like trying to hava a Java interface with two void run()
methods. To make it work you would have to change the method names to be myRun
and yourRun
which is kludgy at best and impossible if two external libs have already chosen the function name run
and are now clashing.
Having not read the Spectre book I can't say if this directly answers the original question, but you may wish to experiment a bit. Unless you have the clashing namespace/function problem it seems you should get the same results either way. You could also profile your program to determine if one or the other speeds up your program.
Looking at the text more closely now, I can't see that it should make any difference for the use-case he cites. You could experiment to verify the answer.
Upvotes: 4