我在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
一起使用,
但我不知道如何进入内部&#34; lambda \s
。
无论如何,我仍然坚持演习的定理证明部分 - 我似乎无法找到很多以伊德里斯为中心的定理证明文件。我想也许我需要开始查看Agda教程(尽管Idris是我依赖的类型语言,我确信我想学习!)。
答案 0 :(得分:10)
简单的答案是,你做不到。在内涵型理论中,关于功能的推理是相当尴尬的。例如,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
)适用于内置的平等(Idris中的=
,Agda中的≡
,你有一个特殊的monoid(或半群),具有不同的底层相等。这通常通过将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
这个解决方案有不同的权衡,即新的平等不能完全取代内置的(当你需要重写一些术语时,这往往会出现)。但是如果你只想证明你的代码能够完成它应该做的事情(最多是一些自定义的相等),那就太棒了。