编辑后。我的问题是:在静态类型语言中,通常使用哪些惯用的Clojure构造代替总和类型?目前的共识是:如果行为可以统一,则使用协议,否则使用标记对/映射,将必要的断言放在前置条件和后置条件中。
Clojure提供了许多表达乘积类型的方法:向量、映射、记录等等,但是如何表示总和类型,也称为标记联合和变体记录?类似于Haskell中的Either a b
或Scala中的Either[+A, +B]
。
我首先想到的是带有特殊标记的映射:{: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]
这样的东西已经存在了吗?(回答:没有)。