Под ред. Теперь мой вопрос: какие идиоматические конструкции Clojure обычно используются вместо типов сумм в языках статических типов? Консенсус до сих пор: использовать протоколы, если поведение может быть унифицировано, использовать помеченные пары/карты в противном случае, поставить необходимые утверждения в предварительном и послесловии.
Clojure предоставляет множество способов выражения типов продуктов: векторов, карт, записей..., но как вы представляете типы сумм , также известных как помеченные союзы и варианты записей? Что-то вроде Either a b
в Haskell или Either[+A, +B]
в Scala.
Первое, что приходит мне в голову, это карта со специальным тегом: {:tag :left :value a}
, но тогда весь код будет загрязнен условностями на (:tag value)
и обрабатывать специальные случаи, если он не существует... То, что я хотел бы гарантировать, состоит в том, что :tag
всегда существует, и он может принимать только одно из указанных значений, а соответствующее значение всегда одного и того же типа/поведения и не может быть nil
, и есть простой способ увидеть, что я позаботился обо всех случаях в коде.
Я могу представить макрос в строках defrecord
, но для типов сумм:
; it creates a special record type and some helper functions
(defvariant Either
left Foo
right :bar)
; user.Either
(def x (left (Foo. "foo"))) ;; factory functions for every variant
; #user.Either{:variant :left :value #user.Foo{:name "foo"}}
(def y (right (Foo. "bar"))) ;; factory functions check types
; SomeException...
(def y (right ^{:type :bar} ()))
; #user.Either{:variant :right :value ()}
(variants x) ;; list of all possible options is intrinsic to the value
; [:left :right]
Существует ли такая вещь? (Ответ: нет).