reify

added
1.2

ns
clojure.core

type
macro

(reify & opts+specs)

reify is a macro with the following structure:

(reify options* specs*)

Currently there are no options.

Each spec consists of the protocol or interface name followed by zero
or more method bodies:

protocol-or-interface-or-Object
(methodName [args+] body)*

Methods should be supplied for all methods of the desired
protocol(s) and interface(s). You can also define overrides for
methods of Object. Note that the first parameter must be supplied to
correspond to the target object ('this' in Java parlance). Thus
methods for interfaces will take one more argument than do the
interface declarations.  Note also that recur calls to the method
head should *not* pass the target object, it will be supplied
automatically and can not be substituted.

The return type can be indicated by a type hint on the method name,
and arg types can be indicated by a type hint on arg names. If you
leave out all hints, reify will try to match on same name/arity
method in the protocol(s)/interface(s) - this is preferred. If you
supply any hints at all, no inference is done, so all hints (or
default of Object) must be correct, for both arguments and return
type. If a method is overloaded in a protocol/interface, multiple
independent method definitions must be supplied.  If overloaded with
same arity in an interface you must specify complete hints to
disambiguate - a missing hint implies Object.

recur works to method heads The method bodies of reify are lexical
closures, and can refer to the surrounding local scope:

(str (let [f "foo"] 
(reify Object 
(toString [this] f))))
== "foo"

(seq (let [f "foo"] 
(reify clojure.lang.Seqable 
(seq [this] (seq f)))))
== (\\f \\o \\o))

reify always implements clojure.lang.IObj and transfers meta
data of the form to the created object.

(meta ^{:k :v} (reify Object (toString [this] "foo")))
== {:k :v}

                (ns foo)

;;; This is a library for the shopping result.

(defrecord Banana [qty])
(defrecord Grape  [qty])
(defrecord Orange [qty])

;;; 'subtotal' differs from each fruit.

(defprotocol Fruit
  (subtotal [item]))

(extend-type Banana
  Fruit
  (subtotal [item]
    (* 158 (:qty item))))

(extend-type Grape
  Fruit
  (subtotal [item]
    (* 178 (:qty item))))

(extend-type Orange
  Fruit
  (subtotal [item]
    (* 98 (:qty item))))

;;; 'coupon' is the function returing a 'reify' of subtotal. This is
;;; when someone uses a coupon ticket, the price of some fruits is 
;;; taken off 25%.

(defn coupon [item]
  (reify Fruit
    (subtotal [_]
      (int (* 0.75 (subtotal item))))))

;;; Example: To compute the total when someone bought 10 oranges,
;;;  15 bananas and 10 grapes, using a coupon for the grapes.
(apply +  (map subtotal [(Orange. 10) (Banana. 15) (coupon (Grape. 10))]))
;;; 4685            ; (apply + '(980 2370 1335))
            
                ;; Using a reified FileFilter implementation to obtain only directory files
(.listFiles (java.io.File. ".")
  (reify
    java.io.FileFilter
    (accept [this f]
      (.isDirectory f))))

            
                ;;;; This example shows how to reify a multi-arity protocol function
;;;; (note the different style in defprotocol vs reify)

;; define a multi-arity protocol function blah
(defprotocol Foo
  (blah
    [this x]
    [this x y]))

;; define an anonymous extension via reify
(def r (reify Foo 
         (blah [_ x] x)
         (blah [_ x y] y)))

;; invoke blah via the r instance
(blah r 1)   ;; => 1
(blah r 1 2)   ;; => 2



            
                ;; Note that nested class is referred via '$' 
;; and 'this' is always present in parameters (see underscore in parameters list):
(Thread/setDefaultUncaughtExceptionHandler
    (reify java.lang.Thread$UncaughtExceptionHandler
      (uncaughtException [_ thread throwable]
        (println (str throwable)))))
            
                ;;; This example is inspired by the above one and simplified
;;; to highlight reify's returning a value/"object" 
;;; with protocol realization for just this one piece of value/"object".

(ns foo)

(defrecord Grape  [qty])

(defprotocol Fruit
  (subtotal [item]))

(extend-type Grape
  Fruit
  (subtotal [item]
    (* 178 (:qty item))))

;;; 'discounted' is the function returning a 'reify' instance of fruit with modified 
;;; implementation of subtotal (with discount). That is, 
;;; when someone uses a discounted coupon, the price of the fruits is taken off 25%.

(defn discounted [item]
  (reify Fruit
    (subtotal [_]
      (println "modifying subtotal with discount:")
      (int (* 0.75 (subtotal item))))))

;;; Example:
;;; There is 10 pieces of Grape, and the subtotal before discount is
;;; (subtotal (Grape. 10))
;;; => 1780

;;; With discount, then
;;; (subtotal (discounted (Grape. 10)))
;;; => modifying subtotal with discount:
;;; => 1335

            
                (comment
  "reify

   verb | re·ify | \\\\ˈrā-ə-ˌfī, ˈrē-\\\\

   :  to regard (something abstract) as a material or concrete thing")

(defprotocol shape
  "A geometric shape."

  (area [this]
    "Calculates the area of the shape.

     The first argument is required and corresponds to the implicit target
     object ('this' in Java parlance)."))


(defn make-circle
  "Creates a circle (a geometric shape) object."
  [radius]

  (reify shape
    (area [_]
          (* Math/PI radius radius))))

(. (make-circle 8) area)
;;=> 201.06192982974676

(def circle (make-circle 8))

(satisfies? shape circle)
;;=> true

(. circle area)
;;=> 201.06192982974676


(defn make-triangle
  "Creates a triangle (a geometric shape) object."
  [base height]

  (reify shape
    (area [_]
          (* 0.5 base height))))

(def triangle (make-triangle 8 8))

(. triangle area)
;;=> 32.0