那么,重点是什么呢? (关于IT技术的问题)

26

So类型的预期目的是什么?将其译为Agda:

data So : BoolSet where
  oh : So true

So将布尔命题提升为逻辑命题。Oury和Swierstra的入门论文The Power of Pi给出了一个以表格列索引的关系代数的示例。取两个表格的乘积需要它们拥有不同的列,对此他们使用了So

Schema = List (String × U)  -- U is the universe of SQL types

-- false iff the schemas share any column names
disjoint : Schema -> Schema -> Bool
disjoint = ...

data RA : SchemaSet where
  -- ...
  Product : ∀ {s s'} → {So (disjoint s s')} → RA s → RA s' → RA (append s s')

我习惯于为我想证明的有关程序的事情构建证据条款。构建逻辑关系以确保Schema的不相交似乎更自然:

Disjoint : Rel Schema _
Disjoint s s' = All (λ x -> x ∉ cols s) (cols s')
  where cols = map proj₁

So和“proper”证明项相比似乎有严重的劣势:在oh上进行模式匹配不会给你任何信息,你不能使用这些信息来检查另一个术语的类型(是吗?)- 这意味着So值不能有效地参与交互式证明。相比之下,Disjoint的计算效用很有用,它被表示为每一列都不出现在s中的证明列表。

我并不真正相信规范So (disjoint s s')Disjoint s s'更简单易写 - 您必须在没有类型检查器帮助的情况下定义布尔函数disjoint - 而且无论如何,Disjoint在您想要操作其中包含的证据时会很有用。

我还怀疑当您构造一个Product时,So是否节省了精力。为了给出So (disjoint s s')的值,您仍然需要在ss'上进行足够的模式匹配,以满足类型检查器它们实际上是不相交的。抛弃这样生成的证据似乎是一种浪费。

So对于部署它的代码的作者和用户来说都不太方便。那么,在什么情况下我会想要使用So呢?

1个回答

14
如果您已经有一个b : Bool,您可以将其转换为命题:So b,这比b ≡ true要短一些。有时候(我不记得有任何实际情况),没有必要费心去处理正确的数据类型,这个快速解决方案就足够了。

与“适当的”证明术语相比,“So”似乎存在严重的缺点:在oh上进行模式匹配不会给您任何信息,您无法使用它来使另一个项通过类型检查。作为推论,So值不能有用地参与交互式证明。与此形成对比的是Disjoint的计算实用性,它被表示为每个列在s'中不存在于s中的证明列表。

So确实提供了与Disjoint相同的信息——您只需要提取它。基本上,如果disjointDisjoint之间没有矛盾,那么您应该能够编写一个函数So (disjoint s) -> Disjoint s,使用模式匹配、递归和不可能的情况消除。

但是,如果您稍微调整一下定义:

So : Bool -> Set
So true  = ⊤
So false = ⊥

So 变成了一个非常有用的数据类型,因为由于 的 eta-规则,x : So true 立即简化为 tt。这使得可以像使用限制条件一样使用 So :在伪 Haskell 中,我们可以写作:

forall n. (n <=? 3) => Vec A n

如果n处于规范形式(即suc(suc(suc ... zero))),则编译器可以检查n <=? 3,无需证明。在实际的Agda中,它是这样的:

∀ {n} {_ : n <=? 3} -> Vec A n

我在这里的答案中使用了这个技巧(那里是{_ : False (m ≟ 0)})。我想,如果没有这个简单的定义,就不可能编写一个可用的机器版本,在这里描述。

Is-just : ∀ {α} {A : Set α} -> Maybe A -> Set
Is-just = T ∘ isJust

其中T在Agda标准库中是So

此外,在存在实例参数的情况下,So作为数据类型可以用作So作为约束条件:

open import Data.Bool.Base
open import Data.Nat.Base
open import Data.Vec

data So : Bool -> Set where
  oh : So true

instance
  oh-instance : So true
  oh-instance = oh

_<=_ : ℕ -> ℕ -> Bool
0     <= m     = true
suc n <= 0     = false
suc n <= suc m = n <= m

vec : ∀ {n} {{_ : So (n <= 3)}} -> Vec ℕ n
vec = replicate 0

ok : Vec ℕ 2
ok = vec

fail : Vec ℕ 4
fail = vec

4
此外,每个 "So b" 的证明在命题上等同于任何其他证明,但对于编码某个属性 b 的实际“证据”来说,情况并非总是如此。有时您希望是这样的。 - Saizan
@Saizan,说得好。这个属性也被用于我回答中的第二个链接。你有一些不错的使用案例吗? - effectfully
我感觉这里有一个更深层次的问题,涉及到归纳定义的 data 类型和函数递归定义的关系。你能详细说明为什么 Agda 可以推断使用你的定义来创建 So 值,但不能用我的定义创建呢? - Benjamin Hodgson
1
@Benjamin Hodgson,在 datarecord 之间的区别方面,后者具有 eta,而前者则没有。 的任何居民都与 tt 在定义上是相等的:eq: ∀ {x} -> x ≡ tt; eq = refl,因此当 Agda 遇到 _x: ⊤,其中 _x 是一个元变量时,_x 将被实例化为 tt,并且解决了统一问题。但是当 Agda 遇到 _x: So true 时,她无法将 _x 与某些内容统一起来,因为这种机制对于“数据”不起作用。但是,您可以使用实例参数强制执行统一,如上所述。 - effectfully
2
@user3237465 我在这里使用了类似的东西 https://github.com/Saizan/miller/blob/master/Injections/Type.agda#L23,使得 Inj 的命题等价于逐点相等。 - Saizan
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接