问题 当相关类型被伊德里斯的lambda抽象时,我如何证明一个“看似明显”的事实?


我在Idris中编写了一个基本的monadic解析器,以熟悉Haskell的语法和差异。我有基本的工作正常,但我坚持尝试为解析器创建VerifiedSemigroup和VerifiedMonoid实例。

不用多说,这里是解析器类型,Semigroup和Monoid实例,以及VerifiedSemigroup实例的开始。

data ParserM a          = Parser (String -> List (a, String))
parse                   : ParserM a -> String -> List (a, String)
parse (Parser p)        = p
instance Semigroup (ParserM a) where
    p <+> q             = Parser (\s => parse p s ++ parse q s)
instance Monoid (ParserM a) where
    neutral             = Parser (const []) 
instance VerifiedSemigroup (ParserM a) where
    semigroupOpIsAssociative (Parser p) (Parser q) (Parser r) = ?whatGoesHere

我基本上被卡住了 intros,具有以下证明者状态:

-Parser.whatGoesHere> intros
----------              Other goals:              ----------
{hole3},{hole2},{hole1},{hole0}
----------              Assumptions:              ----------
 a : Type
 p : String -> List (a, String)
 q : String -> List (a, String)
 r : String -> List (a, String)
----------                 Goal:                  ----------
{hole4} : Parser (\s => p s ++ q s ++ r s) =
          Parser (\s => (p s ++ q s) ++ r s)
-Parser.whatGoesHere> 

看起来我应该可以使用 rewrite 和...一起 appendAssociative 不知何故, 但我不知道如何“进入”lambda \s

无论如何,我仍然坚持演习的定理证明部分 - 我似乎找不到很多以伊德里斯为中心的定理证明文件。我想也许我需要开始查看Agda教程(尽管Idris是我确信我想要学习的依赖类型的语言!)。


2221
2017-08-07 06:36


起源

要得到你需要的lambda 功能扩展性 (funext : (f, g : a -> b) -> ((x : a) -> f x = g x) -> f = g)。可悲的是,Agda和Idris(据我所知)都无法证明这一说法,因此必须假定它是一个公理。另一种选择是介绍你自己的平等概念(例如 p = q <=> forall s. parse p s = parse q s),但我不认为伊德里斯的 VerifiedSemigroup 有能力处理自定义平等。 - Vitus
谢谢!这有点澄清了一些事情。知道术语, 功能扩展性 我正在引导我对这个想法进行更深入的解释。看起来我会依赖于某个地方或其他地方的公理来使这个经验证的实例工作 - 至少现在。所以我不完全有信心如何继续 - funext = believe me 好像在作弊 - 再说一次,它不像我 需要 对于玩具问题的严格证明,在我过去尝试过的其他语言中,只有在评论中才会有任何证据。 - h.forrest.alexander
由于仍然没有答案:您是否希望我将评论转换为实际答案? - Vitus
我没有时间真正尝试使用你的答案来解决这个问题。我打算试图解决这个问题,然后更新我的问题,但是工作和夏季决赛都会受到影响。如果你有解决方案,我肯定希望看到它! - h.forrest.alexander


答案:


简单的答案是你做不到。在内涵式理论中,关于功能的推理是相当尴尬的。例如,Martin-Löf的类型理论无法证明:

S x + y = S (x + y)
0   + y = y

x +′ S y = S (x + y)
x +′ 0   = x

_+_ ≡ _+′_  -- ???

(据我所知,这是一个实际的定理,而不仅仅是“缺乏想象力的证据”;但是,我找不到我读它的来源)。这也意味着没有更一般的证据:

ext : ∀ {A : Set} {B : A → Set}
        {f g : (x : A) → B x} →
        (∀ x → f x ≡ g x) → f ≡ g

这就是所谓的 功能扩展性:如果你能证明所有参数的结果是相等的(也就是说,函数在扩展上是相等的),那么函数也是相等的。

这可以完美地解决您遇到的问题:

<+>-assoc : {A : Set} (p q r : ParserM A) →
  (p <+> q) <+> r ≡ p <+> (q <+> r)
<+>-assoc (Parser p) (Parser q) (Parser r) =
  cong Parser (ext λ s → ++-assoc (p s) (q s) (r s))

哪里 ++-assoc 是你的联想属性的证明 _++_。我不确定它在战术上的表现如何,但它会非常相似:应用同余 Parser 目标应该是:

(\s => p s ++ q s ++ r s) = (\s => (p s ++ q s) ++ r s)

然后,您可以应用扩展性来获得假设 s : String 和目标:

p s ++ q s ++ r s = (p s ++ q s) ++ r s

然而,正如我之前所说,我们没有函数扩展性(请注意,对于类型理论而言,这不是正确的:扩展类型理论,同伦类型理论和其他理论能够证明这一陈述)。简单的选择是将其视为公理。与任何其他公理一样,您有以下风险:

  • 失去一致性(即能够证明虚假;虽然我认为功能扩展性是可以的)

  • 打破减少(仅对案例分析的功能是什么 refl 在给出这个公理的时候做什么?)

我不确定伊德里斯如何处理公理,所以我不会详细介绍。请注意,如果你不小心,公理会弄乱一些东西。


艰难的选择是使用setoids。 setoid基本上是一种配备自定义相等的类型。这个想法是,而不是拥有 Monoid (要么 VerifiedSemigroup 在您的情况下)适用于内置的平等(= 在伊德里斯,  在Agda中,你有一个特殊的幺半群(或半群),具有不同的底层相等。这通常通过将monoid(半群)运算与相等和一组证明打包在一起来完成,即(在伪代码中):

=   : A → A → Set  -- equality
_*_ : A → A → A    -- associative binary operation
1   : A            -- neutral element

=-refl  : x = x
=-trans : x = y → y = z → x = z
=-sym   : x = y → y = x

*-cong : x = y → u = v → x * u = y * v  -- the operation respects
                                        -- our equality

*-assoc : x * (y * z) = (x * y) * z
1-left  : 1 * x = x
1-right : x * 1 = x

解析器的相等性选择很明确:如果两个解析器的输出同意所有可能的输入,则它们是相等的。

-- Parser equality
_≡p_ : {A : Set} (p q : ParserM A) → Set
Parser p ≡p Parser q = ∀ x → p x ≡ q x

这个解决方案有不同的权衡,即新的平等不能完全取代内置的(当你需要重写一些术语时,这往往会出现)。但是如果你只是想表明你的代码完成它应该做的事情(达到一些自定义相等),那就太棒了。


9
2017-08-17 21:30



非常感谢你这个深入的回答!虽然我(显然)需要留出时间在我自己的代码中应用这些想法来真正感受到它们,但这种反应为我提供了一个非常好的起点,可以考虑依赖类型编程中的内涵类型理论的局限性 - 以及如何使用公理或在setoid中建立新的平等观念。如果我还没有开始使用StackOverflow来提出这个问题 - 也就是说,如果我有至少15个声誉 - 我也会赞成这个答案。再次,谢谢! - h.forrest.alexander
@ h.forrest.alexander:别担心。很高兴我能帮上忙! - Vitus