Объекты на clojure

JavaScript - style

(def point {:x 10 :y 20})

(def spoint {:prototype point :dx 1 :dy 2 :y 200})

Пока магии нет. Сделаем магию:

(defn proto-get
  "Returns object property respecting the prototype chain"
  ([this key] (proto-get this key nil))
  ([this key default]
   (cond
     (contains? this key) (this key)
     (contains? this :prototype) (proto-get (this :prototype) key default)
     :else default)))

Теперь магия работает.

(def point {
    :x 10 
    :y 20
    :getX (fn [this] (proto-get this :x))
    :getY (fn [this] (proto-get this :y))
    :setX (fn [this value] (assoc this :x value))
    :setY (fn [this value] (assoc this :y value))
    :add (fn [this a b] ( + a b))})
(def spoint {
    :prototype point 
    :dx 1 
    :dy 2
    :getX (fn [this] (+ (proto-get this :x) (proto-get this :dx)))
    :getY (fn [this] (+ (proto-get this :y) (proto-get this :dy)))
    })
(defn proto-call
  "Calls object method respecting the prototype chain"
  [this key & args]
  (apply (proto-get this key) this args))

Синтаксис выглядит не самым хорошим образом. JS-like objects.

(defn field
  "Creates field"
  [key] (fn
          ([this] (proto-get this key))
          ([this def] (proto-get this key def))))

(defn method
  "Creates method"
  [key] (fn [this & args] (apply proto-call this key args)))

(defn constructor
  "Defines constructor"
  [ctor prototype]
  (fn [& args] (apply ctor {:prototype prototype} args)))

Если честно, то это выглядит очень страшно и этим настолько страшно пользоваться, что я не хочу это

(section "Syntactic sugar")

(example "Field declaration"
         (defn field
           "Creates field"
           [key] (fn
             ([this] (proto-get this key))
             ([this default] (proto-get this key default)))))
(example "Method declaration"
         (defn method
           "Creates method"
           [key] (fn [this & args] (apply proto-call this key args))))
(example "Fields"
         (def __x (field :x))
         (def __y (field :y))
         (def __dx (field :dx))
         (def __dy (field :dy)))
(example "Methods"
         (def _getX (method :getX))
         (def _getY (method :getY))
         (def _setX (method :setX))
         (def _setY (method :setY))
         (def _add (method :add)))

(example "Points"
         (def point
           {:x 10
            :y 20
            :getX __x
            :getY __y
            :setX (fn [this x] (assoc this :x x))
            :setY (fn [this y] (assoc this :y y))
            :add (fn [this a b] (+ a b))
            })
         (def shifted-point
           {:prototype point
            :dx 1
            :dy 2
            :getX (fn [this] (+ (__x this) (__dx this)))
            :getY (fn [this] (+ (__y this) (__dy this)))
            }))
(example "Fields usage"
         (__x point)
         (__x shifted-point)
         (__dx shifted-point)
         (__dx point 100))
(example "Methods usage"
         (_getX point)
         (_getX shifted-point)
         (_getX (_setX shifted-point 1000))
         (_add shifted-point 2 3))


(section "Constructors")

(example "Constructor declaration"
         (defn constructor
           "Defines constructor"
           [ctor prototype]
           (fn [& args] (apply ctor {:prototype prototype} args))))

(example "Supertype"
         (declare _Point)
         (def _distance (method :distance))
         (def _length (method :length))
         (def _sub (method :sub))
         (def PointPrototype
           {:getX __x
            :getY __y
            :sub (fn [this that] (_Point (- (_getX this) (_getX that))
                                         (- (_getY this) (_getY that))))
            :length (fn [this] (let [square #(* % %)] (Math/sqrt (+ (square (_getX this)) (square (_getY this))))))
            :distance (fn [this that] (_length (_sub this that)))
            })
         (defn Point [this x y]
           (assoc this
             :x x
             :y y))
         (def _Point (constructor Point PointPrototype)))

(example "Subtype"
         (def ShiftedPointPrototype
           (assoc PointPrototype
             :getX (fn [this] (+ (__x this) (__dx this)))
             :getY (fn [this] (+ (__y this) (__dy this)))))
         (defn ShiftedPoint [this x y dx dy]
           (assoc (Point this x y)
             :dx dx
             :dy dy
             ))
         (def _ShiftedPoint (constructor ShiftedPoint ShiftedPointPrototype)))

(example "Instances"
         (def point (_Point 10 20))
         (def shifted-point (_ShiftedPoint 10 20 1 2))
         (_getX point)
         (_getX shifted-point)
         (__x point)
         (__x shifted-point)
         (__dx shifted-point)
         (_length (_Point 4 3))
         (_sub (_Point -1 -2) (_Point 2 2))
         (_distance (_Point -1 -2) (_Point 2 2)))

Java style

Интерфейсы:

(definterface IPoint
  (^Number getX [])
  (^Number getY []))

Имплементация:

(deftype JPoint [x y]
  IPoint
  (getX [this] (.-x this))
  (getY [this] (.-y this)))
(deftype JShiftedPoint [x y dx dy]
  IPoint
  (getX [this] (+ (.-x this) (.-dx this)))
  (getY [this] (+ (.-y this) (.-dy this))))


(def point (JPoint. 10 20))
point
(type point)
(def shifted-point (JShiftedPoint. 10 20 1 2))
shifted-point
(type shifted-point)