How to encapsulate in clojure?

clojure, encapsulation, wrapper

Solution

You can use protocols.

First a Clojure record:

(defprotocol Rectangular
  (left [this])
  (right [this]))

(defrecord Rect [x y w h]
  Rectangular
  (left [this] x)
  (right [this] (+ x w)))

(def my-rect (Rect. 1 2 3 4))
(right my-rect) ;=> 4

Now a Java object:

(import java.awt.Rectangle)

(extend-type Rectangle
  Rectangular
  (left [this] (int (.getX this)))
  (right [this] (int (+ (.getX this) (.getWidth this)))))

(def my-other-rect (Rectangle. 1 2 3 4))
(right my-other-rect) ;=> 4

Problem

I have some clojure code that looks like this: ``` (defn rect [x y w h] {:x x, :y y, :w w, :h h}) (def left :x) (def top :y) (def width :w) (def height :h) (defn right [r] (+ (left r) (width r))) (defn bottom [r] (+ (top r) (height r))) ``` Now the following code seems a bit uncommon: ``` (def left :x) ``` However I don't know any other way to get encapsulation. Suppose, I later want to represent my rect a different way. Then relying on (:x rect) is not a good idea, because :x only works on hashmap's and records, so then I would be leaking implementation details in the api, which at least in OO languages is considered bad practice. Now, if I decide to implement my rect in java instead, it gets even worse, because then I would have to write wrappers like: ``` (defn left [rect] (.getLeft rect)) ``` to make sure the interface doesn't change. How does clojure get around this problem?

Original source