【问题标题】:Why won't the following Agda code typecheck?为什么下面的 Agda 代码不进行类型检查?
【发布时间】:2021-09-30 05:08:44
【问题描述】:

我是 Agda 的新手,对此感到困惑。

open import Data.Vec
open import Data.Nat
open import Data.Nat.DivMod
open import Data.Fin hiding (_+_ ; splitAt)
open import Data.Product
open import Relation.Binary.PropositionalEquality

difference : ∀ m (n : Fin m) → ∃ λ o → m ≡ toℕ n + o
difference zero ()
difference (suc m) zero = suc m , refl
difference (suc m) (suc n) with difference m n
difference (suc m) (suc n) | o , p1 = o , cong suc p1

takeFin : ∀ {A : Set} {m : ℕ} (n : Fin m) → Vec A m → Vec A (toℕ n)
takeFin {A} {m = m} n vec with difference m n
... | o , p rewrite p with splitAt (toℕ n) vec
... | xs , _ , _ = xs

takeFin 函数给出错误信息: m != ℕ 类型的 lhs 检查类型时 {m : ℕ} (n : Fin m) (o : ℕ) (p : m ≡ toℕ n + o) (lhs : ℕ) → lhs ≡ toℕ n + o → {A : Set} (vec : Vec A lhs) → Vec A (toℕ n) 生成的 with 函数的格式正确

但以下函数可以编译

takeFin' : ∀ {A : Set} {m : ℕ} (n : Fin m) → Vec A m → Vec A m
takeFin' {A} {m = m} n a vec with difference m n
... | o , p rewrite p with splitAt (toℕ n) vec
... | xs , ys , _ = xs ++ ys

takeFin'' : ∀ {A : Set} {m : ℕ} (n : Fin m) → A → Vec A m → Vec A (toℕ n)
takeFin'' {A} {m = m} n a vec = replicate a

谁能帮帮我?

【问题讨论】:

    标签: agda


    【解决方案1】:

    正如新的 Agda 用户通常所做的那样,您确实使事情变得比您需要的要复杂得多。您打算证明的内容实际上可以通过更简单的方式完成,如下所示:

    open import Data.Vec
    open import Data.Fin
    
    takeFin : ∀ {a} {A : Set a} {m} {n : Fin m} → Vec A m → Vec A (toℕ n)
    takeFin {n = zero} (x ∷ v) = []
    takeFin {n = suc _} (x ∷ v) = x ∷ takeFin v
    

    您应该始终尝试编写简单的归纳证明,而不是使用不必要的中间引理。

    至于为什么您的版本不进行类型检查(不是编译,而是类型检查),原因在于您的 rewrite 调用是在 m ≡ toℕ n + o 的元素上进行的,而您的目标是 Vec A (toℕ n) 类型并且不包含m 的任何出现。相反,您要做的是在您的上下文中转换 vec 的类型,而 rewrite 仅作用于目标。以下是我将如何使其工作:

    takeFin : ∀ {A : Set} {m} {n : Fin m} → Vec A m → Vec A (toℕ n)
    takeFin {m = m} {n} vec with difference m n
    ... | _ , p = proj₁ (splitAt (toℕ n) (subst (Vec _) p vec))
    

    它可以工作,但正如您所见,它远没有那么优雅(而且它还需要您定义的 difference 函数),更重要的是,它使用了通常不鼓励使用的 subst

    作为旁注,主要是为了好玩,可以使函数更简洁和优雅(但不太容易理解),如下所示:

    open import Function
    
    takeFin : ∀ {A : Set} {m} {n : Fin m} → Vec A m → Vec A (toℕ n)
    takeFin {n = n} = proj₁ ∘ (splitAt (toℕ n)) ∘ (subst (Vec _) (proj₂ (difference _ n)))
    

    这个版本虽然阅读起来要复杂得多,但显示了 Agda 在推断参数值方面的强大功能,因为只有 n 明确给出。

    【讨论】:

    • 非常感谢您。我将在我的代码中使用它。虽然我仍然想知道为什么我的版本不起作用:)
    • 因为它过于复杂,所以我没有花时间尝试找出它为什么不输入检查,但我会尝试这样做并相应地编辑我的答案。
    • 再次感谢。我会研究你的答案并尝试从中学习。
    • 好的@MrO。 dropFin怎么样?我什至不能为它写类型签名。我能想到的最好的方法是 -code dropFin : ∀ {a} {A : Set a} {m} {n} → (i : Fin (m + n)) → Vec A (m + n) → Vec A n
    • 请使用 dropFin 部分编辑您的问题,以便我尝试回答。或者问另一个,因为问题不是要在 cmets 中回答的。但是作为一个提示 dropFin 应该有一个与 takeFin 非常相似的签名,除了它产生一个长度为 m-n 的向量
    猜你喜欢
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    • 1970-01-01
    • 2023-04-03
    • 2019-09-24
    • 1970-01-01
    相关资源
    最近更新 更多