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."
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 could not understand the usage of reify function in Clojure.
What is it used for in clojure?
Could you provide examples?
reify is to defrecord what fn is to defn.
"Ah right...... so what's reify"
Put simply, protocols are lists of functions a datatype should support, records are datatypes and reifications are anonymous datatypes.
Maybe this is long-winded, but reify can't be understood concretely without understanding protocols and types/records: Protocols are a way to use the same name for a function such as conj that actually acts differently when given different arguments ((conj [:b :c] :a) => [:b :c :a] but (conj '(:b :c) :a) => (:a :b :c)). Records are like objects or types (but they act like maps making them awesomer).
More fundamentally, the goal is to solve "The Expression Problem" that is to have the ability to seamlessly add new types of data that work with existing functions, and new functions that work seamlessly with existing data.
So one day you say to yourself, "Self, you should learn what it means to be a duck!" so you write a protocol:
(defprotocol Quacks
(quack [_] "should say something in ducky fashion"))
But it's all too abstract so you 'realify' it:
(def donald (reify Quacks
(quack [_] "Quacks and says I'm Donald")))
Now at last you can experience your creation:
(quack donald) => "Quacks and says I'm Donald"
Then you remember about Daffy:
(def daffy (reify Quacks
(quack [_] (str "Quacks and says I'm Daffy"))))
(quack daffy) => "Quacks and says I'm Daffy"
But by the time you remember about Huey, you realize your mistake and define what a duck is in a reusable way:
(defrecord Duck [name]
Quacks
(quack [_] (str "Quacks and says I'm " name)))
And make new ducks (there are several ways to do it):
(def huey (->Duck "Huey"))
(def duey (Duck. "Duey"))
(def louie (new Duck "Louie"))
(quack huey) => "Quacks and says I'm Huey"
Remember that records act like maps (thank to protocols!):
(:name huey) => "Huey"
But then you remember that ducks must quack and walk so you write another protocol:
(defprotocol Walks
(walk [_] "should walk like a duck"))
And extend the definition of duck
(extend-type Duck
Walks
(walk [_] "waddle waddle"))
(walk louie) => "waddle waddle"
Now we can extend other types to implement the same protocol (teach the same function how to work with other things):
So let's say we want programmers to quack too :-)
(defrecord Programmer [] Quacks
(quack [_] "Monads are simply monoids in a category of endofunctors..."))
(quack (Programmer.)) => "Monads are simply monoids in a category of endofunctors..."
I recommend this great explanation of protocols, an explanation of reify and a chapter on protocols in "Clojure for the Brave and True".
Disclaimer: This is only meant to give an initial understanding of what protocols are, not best practices on how to use them. "Psst! I answered this question largely to teach myself, because until yesterday I had never actually written my own protocol/interface!"
So while I hope that it enhances someone else's learning, I'll heartily welcome criticism or edit-suggestions!".
reify macro allow to create an anonymous class extending java.lang.Object class and/or implementing specified interfaces/protocols. The API docs don't describe the purpose clearly but rather provide the technical details what that macro does. Java interop documentation provides a brief description of the purpose:
As of Clojure 1.2, reify is also available for implementing
interfaces.
Even more information can be found in datatypes documentation where you can find a very detailed description what it does and how it compares to proxy:
While deftype and defrecord define named types, reify defines both an
anonymous type and creates an instance of that type. The use case is
where you need a one-off implementation of one or more protocols or
interfaces and would like to take advantage of the local context. In
this respect it is use case similar to proxy, or anonymous inner
classes in Java.
The method bodies of reify are lexical closures, and can refer to the
surrounding local scope. reify differs from proxy in that:
Only protocols or interfaces are supported, no concrete superclass.
The method bodies are true methods of the resulting class, not
external fns. Invocation of methods on the instance is direct, not
using map lookup. No support for dynamic swapping of methods in the
method map. The result is better performance than proxy, both in
construction and invocation. reify is preferable to proxy in all cases
where its constraints are not prohibitive.
At the moment, I have a completely functional Clojure library which is called from Java.
The way I do this : I have a file that uses gen-class to wrap the entire API as static methods of a single class and passes data in and out in the form of IPersistentVector and IPersistentMap.
Now, however, I'm refactoring the library and putting the functionality behind various Protocols.
I have four protocols, lets call them A, B, C and D. And two defrecords, X and Y. X and Y both implement protocols A, B and C. While Y also implements D.
What do I need to do to make these available to Java? Are these automatically available as Interfaces and Classes? Or do I still have to do the equivalent of the gen-class to make them public?
If not, what is the equivalent of the gen-class :methods clause, where I define the Java types for the arguments to the methods?
Does anyone have a simple example of making Protocols and records available to Java?
defprotocol
Every Clojure protocol is also a Java interface with the same name and methods. If I take an example from ibm developerworks, we see that :
(ns com.amalgamated)
(defprotocol Fulfillment
(invoice [this] "Returns an invoice")
(manifest [this] "Returns a shipping manifest"))
Is equivalent to :
package com.amalgamated;
public interface Fulfillment {
public Object invoice();
public Object manifest();
}
Clojure.org also has some (rather terse) information on this.
A Java client looking to participate in the protocol can do so most
efficiently by implementing the protocol-generated interface. External
implementations of the protocol (which are needed when you want a
class or type not in your control to participate in the protocol) can
be provided using the extend construct:
(extend AType AProtocol
{:foo an-existing-fn
:bar (fn [a b] ...)
:baz (fn ([a]...) ([a b] ...)...)} BProtocol
{...} ...)
definterface
If you are aiming at performance, you could consider using definterface, which use is similar to the protocols. This SO post also has details about how to use it :
(definterface Foo
[^int foo [x ^String y]]
[^void bar [^ints is]])
definterface seem to be faster than protocols.
defrecord
Similarly, records (as well as deftype and definterface) will generate Java Classes.
Again, Clojure.org/datatypes has useful information (emphasis mine) :
deftype and defrecord dynamically generate compiled bytecode for a
named class with a set of given fields, and, optionally, methods for
one or more protocols and/or interfaces. They are suitable for dynamic
and interactive development, need not be AOT compiled, and can be
re-evaluated in the course of a single session. They are similar to
defstruct in generating data structures with named fields, but differ
from defstruct in that: [...]
So yes if will be available from Java.
Just be careful with naming.
As a side note, you may want to have a look at calling Clojure from Java.
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 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