cong 到底怎样才能好类型呢?

How exactly can cong be well-typed?

我在看cong的定义:

cong : ∀ {a b} {A : Set a} {B : Set b} (f : A → B) {x y} → x ≡ y → f x ≡ f y
cong f refl = refl

而且我不明白为什么它的类型很好。特别是,refl 的隐式参数似乎必须是 f xf y。为了让事情更清楚,我写了一个非隐式版本的相等性,并试图复制证明:

data Eq : (A : Set) -> A -> A -> Set where
  refl : (A : Set) -> (x : A) -> Eq A x x

cong : (A : Set) -> (B : Set) -> (f : A -> B) -> 
       (x : A) -> (y : A) -> (e : Eq A x y) -> Eq B (f x) (f y)
cong A B f x y e = refl B (f x)

这会导致类型错误:

x != y of type A when checking that the expression refl B (f x) has type Eq B (f x) (f y)

不出所料。除了 (f x),我还能有什么?我错过了什么吗?

依赖模式匹配为您服务。

如果我们在您的 cong

上打个洞
cong : (A : Set) -> (B : Set) -> (f : A -> B) ->
       (x : A) -> (y : A) -> (e : Eq A x y) -> Eq B (f x) (f y)
cong A B f x y e = {!refl B (f x)!}

看看它,我们会看到

Goal: Eq B (f x) (f y)
Have: Eq B (f x) (f x)

所以值确实不同。但是一旦你在 e:

上进行模式匹配
cong : (A : Set) -> (B : Set) -> (f : A -> B) ->
       (x : A) -> (y : A) -> (e : Eq A x y) -> Eq B (f x) (f y)
cong A B f x y (refl .A .x) = {!refl B (f x)!}

xy 相同的事实被揭露,上下文被悄悄重写:y 的每个出现都被 x 替换,所以往洞里看,我们现在看到了

Goal: Eq B (f x) (f x)
Have: Eq B (f x) (f x)

注意我们可以这样写

cong A B f x .x (refl .A .x) = refl B (f x)

即根本不要绑定 y,只是通过点模式说它与 x 相同。我们通过 e : Eq A x y 上的模式匹配获得了这些信息,因为一旦执行了匹配,我们就知道它实际上是 e : Eq A x x,因为这就是 refl 的类型签名所说的。 Eq A x yEq A x x 的统一导致一个微不足道的结论:y 等于 x 并且整个上下文相应地调整。

这与 Haskell GADT 的逻辑相同:

data Value a where
  ValueInt  :: Int  -> Value Int
  ValueBool :: Bool -> Value Bool

eval :: Value a -> a
eval (ValueInt  i) = i
eval (ValueBool b) = b

当您在 ValueInt 上匹配并获得类型 Inti 时,您还揭示了 a 等于 Int 并将此知识添加到上下文(通过等式约束)使得 aInt 稍后可以统一。这就是我们能够 return i 结果的原因:因为 a 从类型签名和 Int 完美统一,正如我们从 上下文.