【问题标题】:Proving theorems about functions with cases用案例证明有关函数的定理
【发布时间】:2018-05-01 17:40:57
【问题描述】:

假设我们有一个函数merge,它只是合并两个列表:

Order : Type -> Type
Order a = a -> a -> Bool

merge : (f : Order a) -> (xs : List a) -> (ys : List a) -> List a
merge f xs [] = xs
merge f [] ys = ys
merge f (x :: xs) (y :: ys) = case x `f` y of
                                   True  => x :: merge f xs (y :: ys)
                                   False => y :: merge f (x :: xs) ys

我们想证明一些聪明的东西,例如,合并两个非空列表会产生一个非空列表:

mergePreservesNonEmpty : (f : Order a) ->
                         (xs : List a) -> (ys : List a) ->
                         {auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
                         NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) = ?wut

检查孔的类型wut 给我们

wut : NonEmpty (case f x y of   True => x :: merge f xs (y :: ys) False => y :: merge f (x :: xs) ys)

到目前为止是有道理的!因此,让我们按照这种类型的建议继续进行大小写拆分:

mergePreservesNonEmpty f (x :: xs) (y :: ys) = case x `f` y of
                                                    True => ?wut_1
                                                    False => ?wut_2

希望wut_1wut_2 的类型能够匹配merge 的case 表达式的相应分支似乎是合理的(所以wut_1 会类似于NonEmpty (x :: merge f xs (y :: ys)),可以立即满足),但我们的希望落空了:类型与原始 wut 相同。

确实,唯一的方法似乎是使用with-clause:

mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
  mergePreservesNonEmpty f (x :: xs) (y :: ys) | True = ?wut_1
  mergePreservesNonEmpty f (x :: xs) (y :: ys) | False = ?wut_2

在这种情况下,类型将符合预期,但这会导致每个with 分支重复函数参数(一旦with 嵌套,情况会变得更糟),加上with 似乎无法播放使用隐式参数很好(但这本身可能值得提出一个问题)。

那么,为什么case 没有在这里提供帮助,除了纯粹的实现方式之外,还有其他原因与with 的行为不匹配吗?还有其他方法可以编写这个证明吗?

【问题讨论】:

    标签: idris


    【解决方案1】:

    | 左侧的内容只有在新信息以某种方式向后传播到参数时才是必需的。

    mergePreservesNonEmpty : (f : Order a) ->
                             (xs : List a) -> (ys : List a) ->
                             {auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
                             NonEmpty (merge f xs ys)
    mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
      | True = IsNonEmpty
      | False = IsNonEmpty
    
    -- for contrast
    sym' : (() -> x = y) -> y = x
    sym' {x} {y} prf with (prf ())
    -- matching against Refl needs x and y to be the same
    -- now we need to write out the full form
      sym' {x} {y=x} prf | Refl = Refl
    

    至于为什么会这样,我确实认为这只是实现,但更了解的人可能会对此提出异议。

    【讨论】:

    • 我发现传播/影响论点的信息标准有点模糊(比如,每个case 都会影响我们对论点的了解,那么究竟是什么构成了这一点?)。但是,也许,这更像是一种直觉,随着人们编写更多 Idris 代码而得到改进。
    • 由于with 分支中的模式类型,这里的反向传播是参数类型的优化
    • @Cactus 我想我很清楚什么是“精炼”,如果它提供了关于给定类型的构造函数的更精确的信息。但是,在我原来的问题中匹配f x y 的情况下,类型的改进是什么?再说一遍,这不是意味着每个if/case 语句都会以某种方式细化类型吗?
    • @0xd34df00d 什么都没有。这就是这个答案的重点。一些匹配揭示了一些关于参数结构的信息,此时您必须使用完整的形式。这个没有,所以缩写是有效的。我不知道@Cactus 到底想对参数类型说什么,但重要的是参数的结构(表达式等于什么?)和模式的类型。在sym' 中,除非xy 相同,否则Refl 模式不会被很好地键入,因此我们对其进行了改进以使其类型良好。 Bool 上的匹配总是很好地输入,没有告诉我们任何信息。
    • @HTNW 啊,我要学会阅读,不知何故完全错过了第一次缺少| 左侧的所有内容。现在我完全明白你的意思了,谢谢!
    【解决方案2】:

    关于用case证明事情的问题:https://github.com/idris-lang/Idris-dev/issues/4001

    因此,在idris-bi 中,我们最终不得不删除此类函数中的所有cases,并定义与case 条件匹配的单独的顶级助手,例如here

    【讨论】:

    • 您甚至不需要 foo1 间接寻址,我只需在 f 上进行大小写拆分即可获得相同的行为。其实这和我下一个问题很接近了,所以我想我也会问。
    猜你喜欢
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-08
    • 2015-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多