说服Idris关于递归调用总数

时间:2019-02-16 10:25:25

标签: idris totality

我写了以下类型来编码其中所有可能的有理数:

data Number : Type where
    PosN : Nat -> Number
    Zero : Number
    NegN : Nat -> Number
    Ratio : Number -> Number -> Number

请注意,PosN Z实际上编码数字1,而NegN Z编码-1。我更喜欢这样的对称定义,而不是Data.ZZ模块给出的定义。现在,我有一个令人信服的问题,那就是说服Idris加上这些数字就是总数:

mul : Number -> Number -> Number
mul Zero _ = Zero
mul _ Zero = Zero
mul (PosN k) (PosN j) = PosN (S k * S j - 1)
mul (PosN k) (NegN j) = NegN (S k * S j - 1)
mul (NegN k) (PosN j) = NegN (S k * S j - 1)
mul (NegN k) (NegN j) = PosN (S k * S j - 1)
mul (Ratio a b) (Ratio c d) = Ratio (a `mul` b) (c `mul` d)
mul (Ratio a b) c = Ratio (a `mul` c) b
mul a (Ratio b c) = Ratio (a `mul` b) c

plus : Number -> Number -> Number
plus Zero y = y
plus x Zero = x
plus (PosN k) (PosN j) = PosN (k + j)
plus (NegN k) (NegN j) = NegN (k + j)
plus (PosN k) (NegN j) = subtractNat k j
plus (NegN k) (PosN j) = subtractNat j k
plus (Ratio a b) (Ratio c d) =
    let a' = assert_smaller (Ratio a b) a in
    let b' = assert_smaller (Ratio a b) b in
    let c' = assert_smaller (Ratio c d) c in
    let d' = assert_smaller (Ratio c d) d in
    Ratio ((mul a' d') `plus` (mul b' c')) (mul b' d')
plus (Ratio a b) c =
    let a' = assert_smaller (Ratio a b) a in
    let b' = assert_smaller (Ratio a b) b in
    Ratio (a' `plus` (mul b' c)) c
plus a (Ratio b c) =
    let b' = assert_smaller (Ratio b c) b in
    let c' = assert_smaller (Ratio b c) c in
    Ratio ((mul a c') `plus` b') (mul a c')

有趣的是,当我在Atom编辑器中按Alt-Ctrl-R时,一切都很好(即使使用%default total指令)。但是,当我将其加载到REPL时,它警告我plus可能不是总数:

   |
29 |     plus Zero y = y
   |     ~~~~~~~~~~~~~~~
Data.Number.NumType.plus is possibly not total due to recursive path 
Data.Number.NumType.plus --> Data.Number.NumType.plus

从该消息中我了解到,对于模式处理比率中对plus的这些递归调用感到担心。我认为断言a小于Ratio a b等可以解决问题,但事实并非如此,因此Idris可能看到了,但是在其他方面遇到了麻烦。不过,我不知道会是什么。

1 个答案:

答案 0 :(得分:1)

assert_smaller (Ratio a b) a已经为Idris所知(毕竟a是“更大” Ratio a b类型的参数)。您需要证明(或断言)的是,mul的结果在结构上小于plus的参数。

因此它应该与

一起使用
plus (Ratio a b) (Ratio c d) =
    let x = assert_smaller (Ratio a b) (mul a d) in
    let y = assert_smaller (Ratio a b) (mul b c) in
    Ratio (x `plus` y) (mul b d)
plus (Ratio a b) c =
    let y = assert_smaller (Ratio a b) (mul b c) in
    Ratio (a `plus` y) c
plus a (Ratio b c) =
    let x = assert_smaller (Ratio b c) (mul a c) in
    Ratio (x `plus` b) (mul a c)