Некоторые вещи все еще реализованы как интерфейсы Java в Clojure; Я бы сказал, что некоторые из них, вероятно, навсегда останутся такими, чтобы облегчить сотрудничество с кодом Clojure из других языков JVM.
К счастью, при определении типа с помощью deftype
вы можете использовать новый тип для реализации любых необходимых вам Java-интерфейсов (о которых Брайан упоминал в комментарии выше), а также любые методы java.lang.Object
. Пример, соответствующий вашему описанию, может выглядеть так:
(deftype Foo [a b]
clojure.lang.IPersistentCollection
(seq [self] (if (seq a) self nil))
(cons [self o] (Foo. a (conj b o)))
(empty [self] (Foo. [] []))
(equiv
[self o]
(if (instance? Foo o)
(and (= a (.a o))
(= b (.b o)))
false))
clojure.lang.ISeq
(first [self] (first a))
(next [self] (next a))
(more [self] (rest a))
Object
(toString [self] (str "Foo of a: " a ", b: " b)))
Пример того, что вы можете сделать с ним в REPL:
user> (.toString (conj (conj (Foo. [] []) 1) 2))
"Foo of a: [], b: [1 2]"
user> (.toString (conj (conj (Foo. [:a :b] [0]) 1) 2))
"Foo of a: [:a :b], b: [0 1 2]"
user> (first (conj (conj (Foo. [:a :b] [0]) 1) 2))
:a
user> (Foo. [1 2 3] [:a :b :c])
(1 2 3)
Обратите внимание, что REPL печатает его как seq; Я считаю, что это из-за встроенной реализации clojure.lang.ISeq
. Вы можете пропустить его и заменить метод seq
на метод, возвращающий (seq a)
для печатного представления, используя пользовательский toString
. str
всегда использует toString
, хотя.
Если вам нужно настраиваемое поведение функций семейства pr
(включая println
и т. Д.), Вам придется изучить реализацию настраиваемого print-method
для вашего типа. print-method
- это мультиметод, определенный в clojure.core
; взгляните на core_print.clj в источниках Clojure для примеров реализации.