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......
Related
I'd like to hide the details of my persistence layer behind some sort of interface. In Java I would just create an interface and choose the correct implementation in some sort of bootup function. I'm still struggling on how to do that in Clojure. I don't necessarily need any type-safety here, I trust in my unit tests to find any issues there. The best thing I could come up with was to create a map containing anonymous functions with specific keys, like so:
(def crux-db {
:get-by-id (fn [id] (get-obj...))
:save (fn [id obj] (store-obj...))
})
(def fs-db {
:get-by-id (fn [id] (get-obj...))
:save (fn [id obj] (store-obj...))
})
If I'm not missing something, this would allow me to replace the database implementation by def-ing (def db crux-db) or (def db fs-db), as long as all the functions exist in all implementation maps. Somehow I feel like this is not the clojure way but I can't put my finger on it. Is there another way to do this?
Protocols are a way to do that. They let you define what functions should be there. And
you can later implement them for different things with e.g.
a defrecord.
A protocol is a named set of named methods and their signatures, defined using defprotocol:
(defprotocol AProtocol
"A doc string for AProtocol abstraction"
(bar [a b] "bar docs")
(baz [a] [a b] [a b c] "baz docs"))
No implementations are provided
Docs can be specified for the protocol and the functions
The above yields a set of polymorphic functions and a protocol object
all are namespace-qualified by the namespace enclosing the definition
The resulting functions dispatch on the type of their first argument, and thus must have at least one argument
defprotocol is dynamic, and does not require AOT compilation
defprotocol will automatically generate a corresponding interface, with the same name as the protocol, e.g. given a protocol my.ns/Protocol, an interface my.ns.Protocol. The interface will have methods corresponding to the protocol functions, and the protocol will automatically work with instances of the interface.
Since you mentioned crux in your code, you can have a peek at how they
use it
here
and then using defrecords to implement some of
them
There are several ways to achieve this. One way would be to use protocols. The other way would be to just use higher-order functions, where you would "inject" the specific function and expose it like so:
(defn get-by-id-wrapper [implementation]
(fn [id]
(implementation id)
...))
(defn cruxdb-get-by-id [id]
...)
(def get-by-id (get-by-id-wrapper cruxdb-get-by-id))
Also worth mentioning here are libraries like component or integrant which are used to manage the lifecylce of state.
i have a record:
(defrecord Foo [a b])
and an instance method for it
(defn inc-a-field [this] (into this {:a (inc (:a this))}))
is it best practice to define a protocol for that? (since it is Foo specific)
Yes, it's best to define protocol with all desired methods first if you want to attach them to you record type. An alternative is to use ordinary functions with no attachment to your record.
Protocols are very handy for stateful operations. For example, look at carmine connection record implementation.
But if your record is just a map with predefined structure, then it may be better to use ordinary clojure functions instead.
You should also look at this question, it's very similar to yours.
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."
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"
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.