我几天来一直在解决问题,但我的Agda技能不是很强。
我正在尝试在索引数据类型上编写一个函数,该类型仅在特定索引处定义。这仅适用于数据构造函数的某些特化。我无法弄清楚如何定义这样的功能。我试图将我的问题减少到一个较小的例子。
设置涉及自然数列表,其中包含用于见证列表成员的类型,以及用于删除列表成员的函数。
open import Data.Nat
open import Relation.Binary.Core
data List : Set where
nil : List
_::_ : (x : ℕ) -> (xs : List) -> List
-- membership within a list
data _∈_ (x : ℕ) : List -> Set where
here : forall {xs} -> x ∈ (x :: xs)
there : forall {xs y} -> (mem : x ∈ xs) -> x ∈ (y :: xs)
-- delete
_\\_ : forall {x} (xs : List) -> (mem : x ∈ xs) -> List
_\\_ nil ()
_\\_ (_ :: xs) here = xs
_\\_ (_ :: nil) (there ())
_\\_ (x :: xs) (there p) = x :: (xs \\ p)
快速检查删除单例列表的head元素是空列表:
check : forall {x} -> nil ≡ ((x :: nil) \\ here)
check = refl
现在我有一些由列表
索引的包装器数据类型-- Our test data type
data Foo : List -> Set where
test : Foo nil
test2 : forall {x} (xs : List) -> (mem : x ∈ xs) -> Foo (xs \\ mem)
test2
构造函数获取列表和成员资格值,并通过从列表中删除元素的结果为数据类型编制索引。
现在我被困的位置是我想要一个以下签名的功能:
foo : Foo nil -> ℕ
foo = {!!}
即,将Foo
值及其索引专门设为nil
。这适用于test
案例
foo test = 0
第二种情况很棘手。我最初想象的是:
foo : Foo nil -> ℕ
foo test = 0
foo (test2 .(_ :: nil) .here) = 1
但是Agda抱怨xs \\ mem != nil of type List when checking that the pattern test2 .(_ :: nil) .here has type Foo nil
。所以我尝试使用with
- 子句:
foo : Foo nil -> ℕ
foo test = 0
foo (test2 xs m) with xs \\ m
... | nil = 1
... | ()
这会产生相同的错误。我已经尝试了各种排列,但是我无法弄清楚如何使用n \\ m = nil
返回模式的信息。我已经尝试了各种其他类型的谓词,但无法弄清楚如何告诉Agda它需要知道什么。非常感谢一些帮助!感谢。
附加:我在Agda中写了一个证明,其中xs : List
和m : x \in xs
表示xs \\ m = nil
表示xs = x :: nil
和{{m = here
1}},看起来它可能对类型检查器有用,但我不知道如何做到这一点。我必须在pi类型上引入一个相对于依赖性的点类相等,这会使问题变得复杂:
data PiEq {A : Set} {B : A -> Set} : (a : A) -> (b : A) -> (c : B a) -> (d : B b) -> Set where
pirefl : forall {x : A} {y : B x} -> PiEq {A} {B} x x y y
check' : forall {x xs m} {eq : xs \\ m ≡ nil} -> (PiEq {List} {\xs -> x ∈ xs} xs (x :: nil) m here)
check' {xs = nil} {()}
-- The only case that works
check' {xs = ._ :: nil} {here} = pirefl
-- Everything else is refuted
check' {xs = ._ :: (_ :: xs)} {here} {()}
check' {xs = ._ :: nil} {there ()}
check' {xs = ._} {there here} {()}
check' {xs = ._} {there (there m)} {()}
答案 0 :(得分:6)
通过设置数据类型的方式,您无法以任何有意义的方式对具有非平凡索引的值进行实际模式匹配。问题当然是Agda不能(通常)解决xs \\ mem
和nil
的统一。
设置模式匹配的方式,你无法真正提供任何证据来帮助统一算法。但是,您可以通过使索引不受限制并使用另一个带有描述实际限制的证明的参数来确保模式匹配的工作原理。这样,您可以进行模式匹配,然后使用证明来消除不可能的情况。
因此,我们不仅仅拥有foo
,而且还有另外一个带有额外参数的函数(比如foo-helper
):
foo-helper : ∀ {xs} → xs ≡ nil → Foo xs → ℕ
foo-helper p test = 0
foo-helper p (test2 ys mem) with ys \\ mem
foo-helper p (test2 _ _) | nil = 1
foo-helper () (test2 _ _) | _ :: _
然后,您只需提供nil ≡ nil
:
foo : Foo nil → ℕ
foo = foo refl
如果您预计经常进行这种模式匹配,那么改为更改数据类型的定义可能会有所帮助:
data Foo′ : List → Set where
test′ : Foo′ nil
test2′ : ∀ {x} xs ys → (mem : x ∈ xs) → ys ≡ xs \\ mem → Foo′ ys
这样,您可以随时进行模式匹配,因为您没有复杂的索引,并且由于包含的证据,仍然可以消除任何不可能的情况。如果我们想用这个定义写foo
,我们甚至不需要辅助函数:
foo′ : Foo′ nil → ℕ
foo′ test′ = 0
foo′ (test2′ xs .nil mem _) with xs \\ mem
foo′ (test2′ _ ._ _ _ ) | nil = 1
foo′ (test2′ _ ._ _ ()) | _ :: _
并表明这两种数据类型(逻辑上)是等价的:
to : ∀ {xs} → Foo xs → Foo′ xs
to test = test′
to (test2 xs mem) = test2′ xs (xs \\ mem) mem refl
from : ∀ {xs} → Foo′ xs → Foo xs
from test′ = test
from (test2′ xs .(xs \\ mem) mem refl) = test2 xs mem
答案 1 :(得分:0)
为什么不通过
定义foo
foo : Foo nil -> ℕ
foo _ = 0
注意:使用Agda的当前开发版本(https://github.com/agda/agda/commit/06fe137dc7d7464b7f8f746d969250bbd5011489)我收到了错误
I'm not sure if there should be a case for the constructor test2,
because I get stuck when trying to solve the following unification
problems (inferred index ≟ expected index):
xs \\ mem ≟ nil
when checking the definition of foo
当我写
foo test = 0