简体   繁体   English

平等模式匹配

[英]Pattern Match on Equality

I'm sort of new to Idris. 我是伊德里斯的新手。 I've used a little agda before, and I have a heavy background in GHC Haskell. 我之前使用过一点agda,我在GHC Haskell中有很重的背景。 I'm trying to understand why something that works in GHC Haskell doesn't work in Idris. 我试图理解为什么在GHC Haskell中有效的东西在Idris中不起作用。 The following code does not compile (idris version 0.12.3, nobuiltins, noprelude): 以下代码无法编译(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

This fails with the following error: 此操作失败,并显示以下错误:

idris_binary.idr:21:3-7:When checking left hand side of IdrisBinary.case block in rightIdentityAlt at idris_binary.idr:20:31: Unifying y and plus y Z would lead to infinite value idris_binary.idr:21:3-7:当在rightIdentityAlt中检查IdrisBinary.case块的左侧时,在idris_binary.idr:20:31:统一y并加上y Z将导致无限值

The roughly equivalent GHC haskell code does typecheck. 大致相当的GHC haskell代码做了类型检查。 I can get the Idris version to typecheck if I instead use: 如果我改为使用以下内容,我可以将Idris版本改为typecheck:

cong : (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)

I think that the reason that rightIdentityAlt works in GHC Haskell but not in Idris deals with a difference in how unification work in the two language. 我认为rightIdentityAlt在GHC Haskell中工作但在Idris中不工作的原因在于统一工作在两种语言中的区别。 In GHC Haskell, the unifications learned from a pattern matching on a GADT just propogate everywhere, but in Idris, it seems like you need to refine the original types with a with clause. 在GHC Haskell中,从GADT上的模式匹配中学到的统一只是传播到任何地方,但在Idris中,似乎需要使用with子句来优化原始类型。 Here's my attempt at doing that: 这是我尝试这样做的:

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

Blast. 爆破。 Still no good. 仍然没有好处。 Now we've lost the equality we were originally trying to prove: 现在我们已经失去了我们最初试图证明的平等:

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

I understand that the pragmatic answer is just to rewrite this using cong or to so something involving tactics, but I really want to understand why the way I want to write rightIdentityAlt doesn't work. 我理解,实用的答案只是用cong或者用涉及策略的东西来重写它,但我真的想要理解为什么我想写的方式rightIdentityAlt并不起作用。 Pattern matching on = isn't bringing evidence into scope the way I would expect it to. 模式匹配=不会像我期望的那样将证据纳入范围。 Is there a way to get it to do this, or is there something fundamentally wrong with this approach in Idris? 有没有办法让它做到这一点,或者这种方法在伊德里斯有根本错误吗?

I think this can be related to Hasochism . 我认为这可能与Hasochism有关。

The word Hasochism was used by Lindley and McBride to descibe the pain and pleasure of using (pseudo-) dependent types like GADTs in Haskell. Lindley和McBride使用Hasochism一词来描述在Haskell中使用(伪)依赖类型(如GADT)的痛苦和乐趣。 In Haskell, as soon as we match on Refl , GHC invokes a theorem prover which will propagate that equality for us. 在Haskell中,一旦我们在Refl匹配,GHC就会调用一个定理证明器,它将为我们传播这个等式。 This is the "pleasure" part. 这是“快乐”的一部分。

The "pain" part lies in not having full dependent types. “痛苦”部分在于没有完全依赖类型。 We do not really have f : (x : T) -> ... in Haskell. 我们在Haskell中确实没有f : (x : T) -> ... If x is universally quantified, it has to be a type in Haskell, and it will be erased at runtime, so we can't pattern match on it directly. 如果x是普遍量化的,它必须是Haskell中的一个类型,它将在运行时被擦除,所以我们不能直接对它进行模式匹配。 We have to use singletons, and other techniques. 我们必须使用单身人士和其他技术。 Also, in Haskell we can't write g : (h : *->*) (x : *) -> hx -> ... and pass the first two arguments to make hx = Int . 另外,在Haskell中我们不能写g : (h : *->*) (x : *) -> hx -> ...并传递前两个参数来使hx = Int For that, h would need to be a type-level function, eg g (\\t:* -> t) Int 42 , but we haven't those. 为此, h需要是一个类型级函数,例如g (\\t:* -> t) Int 42 ,但我们没有那些。 The lack of this feature greatly simplifies the "pleasure" part, though, and the type erasure makes the language more efficient (even though we should have the option of avoiding the erasure, with pi types), so it's not that bad. 然而,缺少这个特性大大简化了“愉悦”部分,而类型擦除使得语言更有效(即使我们应该选择避免删除,使用pi类型),所以它并没有那么糟糕。

Anyway, in Agda/Coq/Idris, unless you want to use some automagic stuff (like tactics), you have to write your own dependent elimination, and bring your equality proofs around where you need them, eg using your cong . 无论如何,在Agda / Coq / Idris中,除非你想使用一些自动化的东西(比如战术),你必须编写自己的依赖消除,并将你的平等证据带到你需要的地方,例如使用你的cong

As an alternative, this also compiles: 作为替代方案,这也编译:

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

Note the innermost function aux , which involves two variables m and n . 注意最里面的函数aux ,它涉及两个变量mn This is done so, when matching against Refl , this results in substituting m with plus n Z without affecting the n . 这样做,当与Refl匹配时,这导致用plus n Z代替m而不影响n To play this "trick" we need two distinct variables. 要发挥这种“技巧”,我们需要两个不同的变量。

The issue with the original code is that m and n , there, are multiple occurrences of the same variable n . 原始代码的问题是mn存在同一个变量n多次出现。 This makes the dependent matching to replace both with S y , and check the resulting type, which triggers the error. 这使得依赖匹配用S y替换它们 ,并检查结果类型,这会触发错误。

Personally, I can understand better the dependent pattern matching in Coq, where you can use match .. return ... to express the resulting type of each match. 就个人而言,我可以更好地理解Coq中的依赖模式匹配,您可以使用match .. return ...来表示每个匹配的结果类型。 Further, that is an expression which can be nested, without requiring separate definitions. 此外,这是一个可以嵌套的表达式,而不需要单独的定义。 Here it is, annotated with some comments showing how each match affects the required type. 这里注释了一些注释,显示每个匹配如何影响所需的类型。

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
.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM