Records and Protocols






cycognito



clojure logo

Materials

Practice

Create two records Doctor and Mr that implement three methods:

  1. (title this) - Returns the title of the person (e.g. "Dr. Jekyll" or "Mr Hyde")

  2. (greet this greeting) - Return personal greeting (e.g. "Hello Dr. Jekyll")

  3. (rename this new-name) - Change the name of a person

Guidelines:

  • Test your code with the unit tests on next slide

  • Define a protocol for each method

  • Let Doctor and Mr give different names to their arguments

  • Implement some of the record methods inside the record.

  • Implement some of the record methods outside the record (with extend-type).

  • When you test your code, explore the different ways to instantiate a record

  • πŸ‚ Try to avoid implementing twice the exact same code (e.g. greet)

Practice - Unit tests

(assert (= (title (Doctor. "Jekyll"))
           "Dr. Jekyll"))
(assert (= (title (Mr. "Hyde"))
           "Mr. Hyde"))
(assert (= (greet (map->Doctor {:name "Jekyll"}) "Hello")
           "Hello Dr. Jekyll"))
(assert (= (greet (map->Mr. {:my-name "Hyde"}) "Hello")
           "Hello Mr. Hyde"))
[source.eval-clojure, clojure]
(assert (= (title (rename (Doctor. "Jekyll") "Manhattan"))
           "Dr. Manhattan"))
(assert (= (title (rename (Mr. "Hyde") "T"))
           "Mr. T"))

Solution

(comment
(defprotocol IWithTitle
  (title [this]))

(defprotocol IGreet
  (greet [this greeting]))

(defprotocol IRenamable
  (rename [this new-name]))

(defn default-greet [person greeting]
  (str greeting " " (title person)))


(defrecord Doctor [name]
  IWithTitle
  (title [doctor] (str "Dr. " name))
  IGreet
  (greet [doctor greeting] (default-greet doctor greeting))
  IRenamable
  (rename [doctor new-name] (assoc doctor :name new-name)))


(defrecord Mr [my-name]
  IWithTitle
  (title [this] (str "Mr. " my-name))
  IGreet
  (greet [doctor greeting] (default-greet doctor greeting)))

(extend-type Mr
  IRenamable
  (rename [doctor new-name] (assoc doctor :my-name new-name)))
  )

Deep insights

β˜• No inheritance

β˜• Defer implementation to a function that could be shared between records

β˜• Records are immutable.

β˜• It is better to have small protocols

β˜• Usually, the name of a protocol starts with a I

β˜• Inside a protocol, the object is named this while inside a records it is named specifically.

β˜• Usually records are defined in separate namespaces

β˜• When to use records instead of maps?

β˜• Records can be extended from the outside

β˜• A protocol is an aggregation of function signatures

β˜• Various ways to instantiate a record