Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to encapsulate in clojure?

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?

like image 354
Mosterd Avatar asked Jul 18 '13 13:07

Mosterd


2 Answers

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
like image 174
A. Webb Avatar answered Nov 15 '22 15:11

A. Webb


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.

like image 31
Jeremy Avatar answered Nov 15 '22 15:11

Jeremy