Suppose I have two protocols:
(defprotocol A
(f [this]))
(defprotocol B
(g [x y]))
And I want to extend protocol B to all instances that support protocol A:
(extend-protocol A
String
(f [this] (.length this)))
(extend-protocol B
user.A
(g [x y] (* (f x) (f y))))
The primary motivation is to avoid having to extend B separately to all the possible classes that A may be extended to, or even to unknown future classes that other people may extend A to (imagine if A was part of a public API, for example).
However this doesn't work - you get something like the following:
(g "abc" "abcd")
=> #<IllegalArgumentException java.lang.IllegalArgumentException:
No implementation of method: :g of protocol: #'user/B found for
class: java.lang.String>
Is this possible at all? If not, is there a sensible workaround to achieve the same objective?
Protocols are not types, and do not support inheritance. A protocol is essentially a named collection of function definitions (and a dispatch mechanism when those functions are called).
If you have multiple types that all happen to have the same implementation, you can simply call a common function. Alternately, you can create a method map and extend each type with that map. E.g.:
(defprotocol P
(a [p])
(b [p]))
(deftype R [])
(deftype S [])
(deftype T [])
(def common-P-impl
{:a (fn [p] :do-a)
:b (fn [p] :do-b)})
(extend R
P common-P-impl)
(extend S
P common-P-impl)
(extend T
P common-P-impl)
If you provide some more detail on your actual scenario, we may be able to suggest the correct approach.
It seems to me that you can implement the function g in terms of f. If that is the case you have all the polymorphism you need without the protocol B.
What I mean is the following, given that f is polymorphic, then
(defn g [x y]
(* (f x) (f y)))
yields a function g which supports all types which implements the protocol A.
Often, when protocols are at the very bottom, simple functions defined only in terms of protocol functions (or on other functions which themself use the protocol) makes the whole namespace/library/program very polymorphic, extendable and flexible.
The sequence library is a great example of this. Simplified, there are two polymorphic functions, first and rest. The rest of the sequence library is ordinary functions.
in "Clojure applied" there's a recipe of extending protocol by protocol
(extend-protocol TaxedCost
Object
(taxed-cost [entity store]
(if (satisfies? Cost entity)
(do (extend-protocol TaxedCost
(class entity)
(taxed-cost [entity store]
(* (cost entity store) (+ 1 (tax-rate store)))))
(taxed-cost entity store))
(assert false (str "Unhandled entity: " entity)))))
actually nothing prevents you from simply extending protocol by another one
(extend-protocol TaxedCost
Cost
(taxed-cost [entity store]
(* (cost entity store) (+ 1 (tax-rate store)))))
while book says it's not possible. I've talked with Alex Miller about this and he said the following:
It really doesn’t work in all cases. The protocol generates a Java interface and you can for sure, extend a protocol to that interface.
The problem is that not every protocol implementation implements that interface - only records or types that do so with an inline declaration like (defrecord Foo [a] TheProtocol (foo ...)). If you are implementing a protocol by using extend-type or extend-protocol, then those instances will NOT be caught by the extension of a protocol interface. So, you really shouldn’t do this :)
From what I see, protocols can indeed extend protocols.
I made an example here: https://github.com/marctrem/protocol-extend-protocol-example/blob/master/src/extproto/core.clj
In the example, we have an Animalia protocol (which allow its members do dream) which is extended by a Erinaceinae protocol (which allows its members to go-fast).
We have a record Hedgehog which is part of the Erinaceinae protocol. Our instance of the record can both dream and go-fast.
(ns extproto.core
(:gen-class))
(defprotocol Animalia (dream [this]))
(defprotocol Erinaceinae (go-fast [this]))
(extend-protocol Animalia
extproto.core.Erinaceinae
(dream [this] "I dream about things."))
(defrecord Hedgehog [lovely-name]
Erinaceinae
(go-fast [this] (format "%s the Hedgehog has got to go fast." (get this :lovely-name))))
(defn -main
[& args]
(let [my-hedgehog (Hedgehog. "Sanic")]
(println (go-fast my-hedgehog))
(println (dream my-hedgehog))))
;1> Sanic the Hedgehog has got to go fast.
;1> I dream about things.
Although I do not completely understand what you are trying to do, I wonder if clojure multimethods would be a better solution for your problem.
Related
Sometimes it is convenient to have an inheritance structure on interfaces:
For example:
I want to define a Functor interface which provides fmap function.
I want to define an Applicative interface which provides fapply and pure functions.
But every applicative functor is a functor: (def fmap #(fapply (pure %1) %2)).
The first solution I came to is the following:
Define Functor and Applicative as independent protocols.
Define functor? function which returns true for any Functor or Applicative instance.
Define fmap mulimethod which can take Functor or Applicative and dispatches
to #(functor/fmap %1 %2) or #(applicative/fapply (applicative/pure %1) %2).
However this solutions smells as it looks like cheating a clojure type system.
The second solution is to define a macro extend-applicative which will automatically implement Functor protocol. But this solution do not look great also as it requires additional work from user of the library and it allows sepparate definition of functor/applicative instances which can easily lead to an error.
Is there a better way to express this kind of relations in clojure?
EDIT: As Thumbnail says, this turned out to be a faulty approach - I was only testing records implementing the protocol interface and didn't notice that extending the Applicative protocol didn't actually implement Functor.
Remember, extend does not require that the type being extended is a concrete implementation; one protocol can extend anotheryou can extend interfaces and abstract classes, too. You should be able to do something like
Applicative.clj
(ns protocol.applicative)
(defprotocol Applicative
(fapply [f g])
(pure [x] ))
functor.clj
(ns protocol.functor
(:import [protocol.applicative.Applicative])
(:require [protocol.applicative :refer [fapply pure]])
)
(defprotocol Functor
(fmap [this f]))
(extend-protocol Functor
protocol.applicative.Applicative
(fmap [this f] (fapply (pure f) this)))
Multiple files and importing is my attempt to deal with some compilation order issues which come up; you'll also have to add the two to AOT. Hope that helps you get it working.
I'm new to clojure and I'm trying to make sense of the different design choices available in different situation. In this particular case I would like to group tightly coupled functionality and make it possible to pass the functions around as a collection.
When to use function maps to group tightly related functionality and when to use protocols (+ implementations)?
What are the advantages and drawbacks?
Is either more idiomatic?
For reference, here are two examples of what I mean. With fn maps:
(defn do-this [x] ...)
(defn do-that [] ...)
(def some-do-context { :do-this (fn [x] (do-this x)
:do-that (fn [] (do-that) }
and in the second case,
(defprotocol SomeDoContext
(do-this[this x] "does this")
(do-that[this] "does that")
(deftype ParticularDoContext []
SomeDoContext
(do-this[this x] (do-this x))
(do-that[this] (do-that))
It all depends on what you meant by "tightly related functionality". There can be 2 interpretations:
These set of functions implement a particular component/sub-system of the system. Example: Logging, Authentication etc. In this case you will probably use a clojure namespace (AKA module) to group the related functions rather than using a hash map.
These set of functions work together on some data structure or type or object etc. In this case you will use Protocol based approach, which allows ad-hoc polymorphism such that new types can also provide this set of functionality. Example: Any interface kind of thing: Sortable, Printable etc.
Protocols are like Interfaces so if what you're trying to create is one, use a protocol.
If you're just trying to group related functions somewhere use a namespace, it's ok to have your functions floating there attached to no particular Object.
It seems to me you're thinking about Objects and using the map just to simulate an Object or a struct you're attaching your functions together. Feels unnatural to me unless it's indeed a type or protocol, and you should use defrecord, deftype and defprotocol in those cases.
An example taken from here about using defprotocol and defrecord:
(defprotocol IAnimal
"the animal protocol"
(inner-report [o] "a report"))
(defrecord Dog []
IAnimal
(inner-report [o]
(println "Woof Woof.\n")))
(defrecord Cat []
IAnimal
(inner-report [o]
(println "Meow Meow.\n")))
My first impression would be that the first way is data oriented and the second way is type oriented. So far, I prefer data oriented.
Perhaps the decision is related with Alan Perlis quote: "It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures."
I have a protocol and several deftypes implementing it within one workspace. How can I list all deftypes that implement following protocol?
I've came to the solution that filters data from (ns-public), but I don't like it, because it uses some "magic" to get the work done, as I haven't found proper way to achieve my goal with satisfies? and extends?.
Any ideas?
(defprotocol Protocol
(foo[this] "just an interface method"))
(deftype Dummy [] Protocol
(foo[this] "bar"))
(defn implements? [protocol atype] "fn from clojure sources"
(and atype (.isAssignableFrom ^Class (:on-interface protocol) atype)))
(defn list-types-implementing[protocol]
(filter (fn[x] (let [[a b] x]
(when (.startsWith (str a) "->") ; dark magic
(implements? protocol
(resolve (symbol
(.replace (str a) "->" "")))))
))
(ns-publics *ns*)))
(list-types-implementing Protocol) ; => ([->Dummy #'user/->Dummy])
(let [[a b] (first(list-types-implementing Protocol))]
(foo (b)) ; => "bar"
)
In general, this is going to be a hairy problem to solve because there are two different ways a type can satisfy a protocol. You can extend any existing Java class to a protocol using the extend-type and extend-protocol functions (this is a very powerful feature because it allows you to extend your code to work with built-in Java or Clojure types, or other third-party types that you don't control). Or, you can specify a protocol implementation directly as part of a type definition in deftype or defrecord. These two mechanisms are implemented differently.
The first case (extension via extend-type or extend-protocol) is going to be easier for you to solve because the type being extended is going to be attached to the protocol itself (a protocol essentially amounts to a generated Java interface plus a Clojure map with metadata about the protocol). You can find the types that extend the protocol by looking at the :impls key in the protocol map:
user=> (defprotocol MyProtocol (foo [this] "Protocol function"))
user=> (deftype MyType [])
user=> (extend-type MyType MyProtocol (foo [_] "hello foo!"))
user=> (keys (:impls MyProtocol))
(user.MyType)
The second case (directly implementing a protocol via deftype or defrecord) is more difficult because what's happening is the Java class generated for the type or record will directly implement the Java interface defined by the protocol (you can implement any Java interface in a deftype or defrecord, not just a protocol). Any approach to find types that extend a protocol in this manner is going to require some deal of scanning and reflection. Essentially what you're asking is, "how can I find all the Java classes implementing a given interface?"
In a typical Java application, you might do something along the lines of scanning the directories of the classpath for .class files and introspecting on them, but this won't work in Clojure because there very well might not be .class files for your types (the bytecode is generated dynamically). If you're not satisfied with your own implementation, you might check out the answer in this question and see if it's more to your liking:
Find Java classes implementing an interface
defn = public
defn- = private
Perhaps I have bad Clojure coding style -- but I find that most functions I write in Clojure are small helper functions that I do not want to expose.
Is there some configuration option, where:
defn = private by default,
and to make something public, I have to do defn+?
Thanks!
No. There is not.
An alternative approach which might or might not work for you is to declare a foo.bar.internal namespace containing all the private helpers which is used by your foo.bar namespace. This has advantages over private function declarations when you want to use private functions in macro expansions.
If the "helper functions" are very likely to be used once, you could choose to make them locals of your bigger functions, or write them as anonymous functions. See letfn: http://clojuredocs.org/clojure_core/clojure.core/letfn and http://clojuredocs.org/clojure_core/clojure.core/fn.
I hardly ever use letfn myself.
The beauty of Clojure being a Lisp, is that you can build and adapt the language to suit your needs. I highly recommend you read On Lisp, by Paul Graham. He now gives his book away for free.
Regarding your suggestion of defn+ vs defn vs defn-. This sound to me like a good case for writing your own Macro. defn is a function and defn- is a macro. You can simply redefine them as you wish, or wrap them in your own.
Here follows a suggestion to implementation, based mainly on Clojure's own implementation - including a simple utility, and a test.
(defmacro defn+
"same as Clojure's defn, yielding public def"
[name & decls]
(list* `defn (with-meta name (assoc (meta name) :public true)) decls))
(defmacro defn
"same as Clojure's defn-, yielding non-public def"
[name & decls]
(list* `defn (with-meta name (assoc (meta name) :private true)) decls))
(defn mac1
"same as `macroexpand-1`"
[form]
(. clojure.lang.Compiler (macroexpand1 form)))
(let [ ex1 (mac1 '(defn f1 [] (println "Hello me.")))
ex2 (mac1 '(defn+ f2 [] (println "Hello World!"))) ]
(defn f1 [] (println "Hello me."))
(defn+ f2 [] (println "Hello World!"))
(prn ex1) (prn (meta #'f1)) (f1)
(prn ex2) (prn (meta #'f2)) (f2) )
As stated by #kotarak, there is no way (as far as I know) to do that, nor is it desirable.
Here is why I dislike defn- :
I found out that when using various Clojure libraries I sometimes need to slightly modify one function to better suit my particular needs.
It is often something quite small, and that makes sense only in my particular case. Often this is just a char or two.
But when this function reuses internal private functions, it makes it harder to modify. I have to copy-paste all those private functions.
I understand that this is a way for the programmer to say that "this might change without notice".
Regardless, I would like the opposite convention :
always use defn, which makes everything public
use defn+ (that doesn't exist yet) to specify to the programmer which functions are part of the public API that he is supposed to use. defn+ should be no different from defnotherwise.
Also please note that it is possible to access private functions anyway :
;; in namespace user
user> (defn- secret []
"TOP SECRET")
;; from another namespace
(#'user/secret) ;;=> "TOP SECRET"
I would like to provide some methods for a clojure protocol starting with the : character. Is there any way to override this in Clojure?
Don't think so. Clojure keywords are implemented in the reader and I don't think there's any way of overriding that behavior.
When you use a keyword as a function, that is equivalent to (get arg :keyword). You can extend what that does by implementing ILookup in your protocol.
Joost.
Sounds like a bad idea: colons are reserved for keywords so even if you could do this I think it would make for some confusing code.
You can of course, put a function inside a record mapped by a keyword:
(defrecord Foo [])
(def foo (Foo. nil {:method (fn [a b] (* a b))}))
((:method foo) 7 10)
=> 70
I've found this to be a useful trick sometimes......