使用Idris中的类型谓词生成运行时证明

时间:2014-11-23 23:12:46

标签: parsing unification idris decidable

我使用这种类型来推断可以执行可解析解析的字符串:

data Every : (a -> Type) -> List a -> Type where
  Nil : {P : a -> Type} -> Every P []
  (::) : {P : a -> Type} -> P x -> Every P xs -> Every P (x::xs)

例如,定义数字[0-9],如下所示:

data Digit : Char -> Type where
  Zero  : Digit '0'
  One   : Digit '1'
  Two   : Digit '2'
  Three : Digit '3'
  Four  : Digit '4'
  Five  : Digit '5'
  Six   : Digit '6'
  Seven : Digit '7'
  Eight : Digit '8'
  Nine  : Digit '9'

digitToNat : Digit a -> Nat
digitToNat Zero  = 0
digitToNat One   = 1
digitToNat Two   = 2
digitToNat Three = 3
digitToNat Four  = 4
digitToNat Five  = 5
digitToNat Six   = 6
digitToNat Seven = 7
digitToNat Eight = 8
digitToNat Nine  = 9

然后我们可以有以下功能:

fromDigits : Every Digit xs -> Nat -> Nat
fromDigits [] k = 0
fromDigits (x :: xs) k = (digitToNat x) * (pow 10 k) + fromDigits xs (k-1)

s2n : (s : String) -> {auto p : Every Digit (unpack s)} -> Nat
s2n {p} s = fromDigits p (length s - 1)

这个s2n函数现在可以在编译时正常工作,但那时它本身并不是很有用。要在运行时使用它,我们必须在使用该函数之前构造证明Every Digit (unpack s)

所以我想我现在想写这样的功能:

every : (p : a -> Type) -> (xs : List a) -> Maybe $ Every p xs

那或我们想要返回会员证明或非会员证明,但我不完全确定如何以一般方式做这些事情。所以我尝试只为字符执行Maybe版本:

every : (p : Char -> Type) -> (xs : List Char) -> Maybe $ Every p xs
every p [] = Just []
every p (x :: xs) with (decEq x '0')
  every p ('0' :: xs) | (Yes Refl)  = Just $ p '0' :: !(every p xs)
  every p (x   :: xs) | (No contra) = Nothing

但后来我得到了这个统一错误:

    Can't unify
            Type
    with
            p '0'

    Specifically:
            Can't unify
                    Type
            with
                    p '0'

p 的类型为Char -> Type 。我不确定导致此统一失败的原因,但认为问题可能与my previous question有关。

这是我正在尝试做什么的合理方法吗?我觉得此刻它的工作量有点大,应该可以使用这些功能的更多通用版本。如果可以使用auto关键字来编写函数,可以使用Maybe proofEither proof proofThatItIsNot,这与DecEq类的工作方式类似。

1 个答案:

答案 0 :(得分:8)

错误消息是正确的:您提供的类型为Type,但您需要类型为p '0'的值。 p类型为Char -> Type,因此p '0'的类型为Type,这也是正确的。但是,p '0'不属于p '0'类型。

使用更简单的类型可能会更容易看到问题:3类型为IntInt类型为Type,但Int没有有类型Int

现在,我们如何解决这个问题?好吧,p是一个谓词,意味着它构造了居民就是这个谓词的证明的类型。因此,我们需要提供的类型p '0'的值将是一个证据,在这种情况下证明'0'是一个数字。 Zero恰好就是这样的证据。但是在every的签名中,p变量并不是在谈论数字:它是一个抽象谓词,我们对此一无所知。出于这个原因,我们没有使用的值代替p '0'。我们必须改变every的类型。

一种可能性是编写every的更专业版本版本,其中一个版本仅适用于特定谓词Digit,而不适用于任意p

everyDigit : (xs : List Char) -> Maybe $ Every Digit xs
everyDigit [] = Just []
everyDigit (x :: xs) with (decEq x '0')
  everyDigit ('0' :: xs) | (Yes Refl)  = Just $ Zero :: !(everyDigit xs)
  everyDigit (x   :: xs) | (No contra) = Nothing

我没有在需要类型p '0'的值的地方错误地使用值p '0',而是在一个现在需要类型为{的值的地方使用了值Zero {1}}。

另一种可能性是修改Digit '0',以便除了为每个every提供证明类型的谓词p之外,我们还会收到证明制作函数{{ 1}}如果可能的话,它会为每个Char提供相应的证明值。

mkPrf

我不再在Char上进行模式匹配,而是要求every : (p : Char -> Type) -> (mkPrf : (c : Char) -> Maybe $ p c) -> (xs : List Char) -> Maybe $ Every p xs every p mkPrf [] = Just [] every p mkPrf (x :: xs) with (mkPrf x) every p mkPrf (x :: xs) | Just prf = Just $ prf :: !(every p mkPrf xs) every p mkPrf (x :: xs) | Nothing = Nothing 检查Char。然后我对结果进行模式匹配,看看它是否找到了证据。 mkPrf上的Char模式匹配是mkPrf的实现。

Char

everyDigit' : (xs : List Char) -> Maybe $ Every Digit xs everyDigit' = every Digit mkPrf where mkPrf : (c : Char) -> Maybe $ Digit c mkPrf '0' = Just Zero mkPrf _ = Nothing 的实现中,我们再次为具体类型mkPrf而不是抽象类型Digit '0'构建证明,因此p '0'是可接受的证明。