我对伊德里斯来说是个新人。我之前使用过一点agda,我在GHC Haskell中有很重的背景。我试图理解为什么在GHC Haskell中有效的东西在Idris中不起作用。以下代码无法编译(idris版本0.12.3,nobuiltins,noprelude):
data Nat = S Nat | Z
plus : Nat -> Nat -> Nat
plus Z right = right
plus (S left) right = S (plus left right)
rightIdentityAlt : (n : Nat) -> n = (plus n Z)
rightIdentityAlt Z = Refl
rightIdentityAlt (S y) = case rightIdentityAlt y of
Refl => Refl
此操作失败,并显示以下错误:
idris_binary.idr:21:3-7:当检查idis_binary.idr中的rightIdentityAlt的IdrisBinary.case块的左侧时:20:31: 统一y和加y Z将导致无限值
大致相同的GHC haskell代码做了类型检查。如果我改为使用:
,我可以将Idris版本改为typecheckcong : (x : Nat) -> (y : Nat) -> (f : Nat -> Nat) -> x = y -> f x = f y
cong _ _ _ Refl = Refl
rightIdentity : (n : Nat) -> n = (plus n Z)
rightIdentity Z = Refl
rightIdentity (S x) = cong x (plus x Z) S (rightIdentity x)
我认为rightIdentityAlt
在GHC Haskell中工作但在Idris中不起作用的原因是在两种语言中统一工作的不同之处。在GHC Haskell中,从GADT上的模式匹配中学到的统一只是在所有地方传播,但在Idris中,似乎需要使用with
子句来优化原始类型。这是我尝试这样做的:
rightIdentityAlt : (n : Nat) -> n = (plus n Z)
rightIdentityAlt Z = Refl
rightIdentityAlt (S y) with (rightIdentityAlt y)
rightIdentityAlt (S y) | Refl {A = Nat} {x = y} = Refl
高炉。仍然没有好处。现在我们已经失去了我们最初试图证明的平等:
idris_binary.idr:26:20:When checking left hand side of with block in IdrisBinary.rightIdentityAlt:
Type mismatch between
plus y Z (Inferred value)
and
y (Given value)
Holes: IdrisBinary.rightIdentityAlt
我理解,实用的答案只是使用cong
或者涉及战术的东西来重写它,但我真的想理解为什么我想写rightIdentityAlt
的方式不起作用。 =
上的模式匹配并没有像我期望的那样将证据纳入范围。有没有办法让它做到这一点,或者这种方法在伊德里斯有什么根本的错误吗?
答案 0 :(得分:5)
我认为这可能与Hasochism有关。
Lindley和McBride使用Hasochism一词来描述在Haskell中使用(伪)依赖类型(如GADT)的痛苦和乐趣。在Haskell中,只要我们在Refl
上匹配,GHC就会调用一个定理证明器,它将为我们传播这个等式。这是"愉快"一部分。
"痛苦"部分在于没有完全依赖类型。我们在Haskell中确实没有f : (x : T) -> ...
。如果x
是普遍量化的,它必须是Haskell中的一个类型,并且它将在运行时被删除,因此我们不能直接对它进行模式匹配。我们必须使用单身人士和其他技术。此外,在Haskell中,我们不能写g : (h : *->*) (x : *) -> h x -> ...
并将前两个参数传递给h x = Int
。为此,h
需要是类型级函数,例如g (\t:* -> t) Int 42
,但我们还没有。缺乏此功能大大简化了“快乐”。但是,部分删除和类型擦除使语言更有效(即使我们应该选择使用pi
类型来避免擦除),所以它并没有那么糟糕。
无论如何,在Agda / Coq / Idris中,除非你想使用一些自动化的东西(比如战术),否则你必须编写自己的依赖消除,并将你的平等证据带到你需要的地方,例如:使用cong
。
作为替代方案,这也可以编译:
rightIdentityAlt : (n : Nat) -> n = (plus n Z)
rightIdentityAlt Z = Refl
rightIdentityAlt (S y) = aux y (rightIdentityAlt y)
where
aux : (m : Nat) -> m = plus n Z -> S m = plus (S n) Z
aux _ Refl = Refl
请注意最里面的函数aux
,它涉及两个变量m
和n
。这样做,当与Refl
匹配时,这会导致m
替换plus n Z
而不影响n
。要玩这个"技巧"我们需要两个不同的变量。
原始代码的问题是,m
和n
是同一个变量n
的多次出现。这使得依赖匹配将两者替换为S y
,并检查结果类型,从而触发错误。
就个人而言,我可以更好地理解Coq中的依赖模式匹配,您可以使用match .. return ...
来表示每个匹配的结果类型。此外,这是一个可以嵌套的表达式,而不需要单独的定义。这里注释了一些注释,显示每个匹配如何影响所需的类型。
Fixpoint rightIdentityAlt (n: nat): n = plus n O :=
match n return n = plus n O with
| O => (* required: n = plus n O with n := O
hence : O = plus O O *)
eq_refl
| S y => (* required: n = plus n O with n := S y
hence : S y = plus (S y) O *)
match rightIdentityAlt y in _ = o return S y = S o with
| eq_refl => (* required: S y = S o with o := y
hence : S y = S y *)
eq_refl
end
end
.