What is reify in Clojure?
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.