Переопределение equals, hashCode и toString в Clojure deftype

Я пытаюсь создать новый тип в Clojure с помощью deftype для реализации двумерной (x, y) координаты, которая реализует протокол "Location".

Я бы также хотел, чтобы этот инструмент реализовал стандартные методы Java, hashCode и toString.

Моя первоначальная попытка:

 (defprotocol Location   
   (get-x [p])  
   (get-y [p])   
   (add [p q]))


 (deftype Point [#^Integer x #^Integer y]   
     Location
       (get-x [p] x)
       (get-y [p] y) 
       (add [p q] 
         (let [x2 (get-x q)
               y2 (get-y q)]
           (Point. (+ x x2) (+ y y2))))   
     Object
       (toString [self] (str "(" x "," y ")"))
       (hashCode [self] (unchecked-add x (Integer/rotateRight y 16)))
       (equals [self b] 
         (and 
           (XXXinstanceofXXX Location b) 
           (= x (get-x b)) 
           (= y (get-y b)))))

Однако метод equals все еще нуждается в некотором способе разработки, если параметр b реализует протокол местоположения.

Каков правильный подход? Я на правильном пути?

Ответ 1

Чтобы проверить, удовлетворяет ли какой-либо протокол, satisfies?.

Edit:

Протоколы и типы данных слишком новы в Clojure (и по-прежнему быстро развиваются), чтобы я мог много сказать о том, что такое идиоматика или нет. Но вы должны заметить, что defrecord уже реализует равенство типа и ценности. Если вам действительно не нужен специальный хэш-код для ваших объектов, вы можете использовать defrecord.

(defrecord Point [#^Integer x #^Integer y]   
  Location
  (get-x [p] x)
  (get-y [p] y) 
  (add [p q] 
       (let [x2 (get-x q)
             y2 (get-y q)]
         (Point. (+ x x2) (+ y y2)))))

user> (= (Point. 1 2) {:x 1 :y 2})
false
user> (= (Point. 1 2) (Point. 1 2))
true

Вы также получаете дополнительный бонус от доступа к своим полям с помощью поиска по ключевым словам и возможность размещения метаданных на ваших объектах, которые defrecord дает вам бесплатно.

user> (:x (Point. 1 2))
1

Возможно, что defrecord -определенные вещи будут иметь пользовательский синтаксис считывателя когда-нибудь в Clojure, чтобы их можно было печатать с готовностью и читать с помощью читателя Clojure. Если вы действительно не привязаны к своей версии toString, вы можете также помнить об этом. Прямо сейчас записи уже печатаются по-человечески, если не читаются машиной.

user> (Point. 1 2)
#:user.Point{:x 1, :y 2}