对于
添加 a b === 添加 b a
您需要按如下方式交换 AddL/AddR 构造函数:
to :: Add a b -> Add b a
to (AddL x) = AddR x
to (AddR x) = AddL x
-- from = to
对于
添加无效a === a
你需要一个多态函数void : Void -> a
to :: Add Void a -> a
to (AddL impossible) = void impossible
to (AddR x) = x
from :: a -> Add Void a
from x = AddR x
变量impossible 代表Void 类型的“不存在”值。确实没有这样的价值(除了底部/不确定性)。这就是为什么这条线
to (AddL impossible) = ...
实际上是无法访问的代码——它永远不会被执行。
函数void利用了这样一个事实,即它需要一个不可能的参数才能凭空产生一个值a。遗憾的是,在 Haskell 中,void 不能被定义,除非利用不确定性,例如
void :: Void -> a
void _ = error "This will never be reached"
一个更优雅和正确的解决方案应该是
void :: Void -> a
void x = case x of
-- no constructors for Void, hence no branches here to do!
-- since all these (zero) branches have type `a`, the whole
-- case has type `a` (!!!)
但是,唉,Haskell 禁止空的 case 构造。 (正如bheklilr 指出的那样,在 GHC 7.8 中,通过 EmptyCase 扩展名允许空箱。
相比之下,在 Coq 或 agda 等依赖类型语言中,上面的代码(稍作改动)会很好。这是 Coq 中的:
Inductive Void : Set := . (* No constructors for Void *)
Definition void (A : Set) (x : Void) : A :=
match x with
end .
在阿格达
data Void : Set where
-- no constructors
void : (A : Set) -> Void -> A
void A ()
-- () is an "impossible" pattern in Agda, telling the compiler that this argument
-- has no values in its type, so one can omit the definition entirely.