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?
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
Clojure has protocols and types. You identify an abstraction with a protocol (like Java interfaces) and you create implementations for those abstractions. There also are multimethods which give you more freedom over how method dispatch occurs.
However, structuring your data in the way you have in your example is very common. Take a look at the Leiningen project file and the contents of Ring requests and responses. Even if you decided to completely change how you lookup the values of x and y, you can simply create a type that operates on the same abstraction as a map. In this case, that's the ILookup protocol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With