如何在Idris中表达范围有效性?

时间:2017-03-23 11:19:01

标签: dependent-type idris

我正在尝试在Idris中建模一个简单的调查表,目前正在努力验证用户输入,这是一个字符串,w.r.t。问的类型。

目前我有以下类型:

data Question : Type where
  QCM : {numOptions : Nat}
      -> (question : String) 
      -> (qcmOptions : Vect numOptions String)
      -> (expected : Fin numOptions)
      -> Question

data Answer : (q : Question) -> Type where
   AnswerQCM : (option : Fin n) -> Answer (QCM {numOptions = n } q opts exp)

total 
isCorrectAnswer : (q : Question ) -> Answer q -> Bool
isCorrectAnswer (QCM {numOptions} question qcmOptions expected) (AnswerQCM option) = option == expected


data IsAnswer : (s : String) -> (q : Question) -> Type where
     ValidQCM : (option : Fin n) -> IsAnswer s (QCM {numOptions = n } q opts exp)

notInRange : (num : Fin n) -> { auto prf : GT numOptions n } 
           -> IsAnswer s (QCM {numOptions} q opts exp) -> Void
notInRange num x = ?notInRange_rhs

我没有看到如何编写notInRange函数,该函数应该证明某些数字可能不是多项选择问题调查的有效答案:此数字应该在正确的范围内问题中提供的选项数量。

更一般地说,我想编写一个看似如下的readAnswer函数:

readAnswer : (s : String) -> (q : Question) -> Dec (IsAnswer s q)
readAnswer s (QCM question qcmOptions expected) = ?readAnswer_rhs_1

我似乎很难找到contra的{​​{1}}部分,因为我的类型没有正确地表达我想要的限制,以至于其中一些可以被证明无人居住。

1 个答案:

答案 0 :(得分:4)

嗯,回答你的问题非常重要。你有几个设计问题。我不会只粘贴结果代码。 Intead我会尝试解释当前的方法发生了什么以及存在什么问题。也许我的解决方案并不完全符合你的要求(也许你甚至不需要你想要的东西),但我希望它对你有帮助。

一个大问题是你的isCorrectAnswer功能。它返回Bool并且Bool对于证明是不好的,因为编译器对Bool的值知之甚少。如果您需要更强的证明能力,则应使用Refl代替True/False。与MaybeDec相同的情况。如果您已经足够Maybe那么就可以了,但如果您希望您的实施可以证明您应该使用Dec而不是Maybe。一旦你决定在你的函数中使用Dec,你正在使用和调用它的所有函数也应该包含更可证明的信息。

思考过程的良好开端是考虑具有以下功能:

isCorrectAnswer : (q: Question) -> (a: Answer q) -> expected q = option a 

不幸的是,由于两个问题,这个功能不可能存在。

  1. 这是一个证据,证据就是真实的。如果您有任意QuestionAnswer,那么预期的答案与给定的完全一致并不总是正确的。因此,您应该将此函数转换为具有此类属性的数据类型。这只是一种通用方法。
  2. 嗯,在当前情况下,您甚至无法为expected数据类型编写Question函数。也许有人可以,但我尝试过但失败了。此expected函数不是当前实现中相应字段的名称。而numOptionsQuestion的内部事物。没有模式匹配,从外面看不到它。因此,在矢量长度上参数化Question是件好事。
  3. 要解决2,我将以下一种方式稍微转换您的数据类型:

    record Question (numOptions : Nat) where
        constructor QCM
        question   : String
        qcmOptions : Vect numOptions String
        expected   : Fin  numOptions
    
    record Answer (q : Question n) where
        constructor AnswerQCM
        option : Fin n
    

    所以现在属性看起来像这样:

    data IsCorrectAnswer : (q : Question n) -> (a: Answer q) -> Type where
       IsCorrect : {q: Question n} 
                -> {a: Answer q} 
                -> expected q = option a 
                -> IsCorrectAnswer q a
    

    这是一个简单的决定程序:

    isCorrectAnswer : (q : Question n) -> (a: Answer q) -> Dec (IsCorrectAnswer q a)
    isCorrectAnswer (QCM  _ _ expected) (AnswerQCM option) =
        case decEq expected option of
            Yes prf   => Yes (IsCorrect prf)
            No contra => No (\(IsCorrect prf) => contra prf)
    

    在我的问题中,当我在StringQuestion之间实施时,您需要AnswerQuestion之间的属性。所以现在理论上你可以在qcmOptions中查找此字符串,查找Fin索引,将其转换为答案并获取Dec IsCorrectAnswer。好吧,有点。除非事实证明非常非常重要。只是因为你不能证明你想要的定理的原因。

    在您的数据类型中,{strong} s: String(option : Fin n)之间存在。可能有几个额外的属性和数据类型可以提供帮助。但更简单的解决方案是使用IsAnswer Vect使isCorrectAnswer属性包含更多信息。这里的最终实现与data IsAnswer : (s : String) -> (q : Question n) -> Type where ValidQCM : {q: Question n} -> Elem s (qcmOptions q) -> IsAnswer s q readAnswer : (s : String) -> (q: Question n) -> Dec (IsAnswer s q) readAnswer s (QCM _ options _) = case isElem s options of Yes prf => Yes (ValidQCM prf) No contra => No (\(ValidQCM prf) => contra prf) 几乎相同:

    isCorrectAnswer

    你可以注意到我在这里没有使用pip uninstall pywin32而我可能不会因为我之前提到的问题而使用pip install <Name of the wheel file with extension>。但我不需要在这里使用它。更简单的解决方案更好。

    P.S。对不起,我可能会写一些简短的答案,但我希望通过这种方式,这个答案可以向你澄清更多的事情。