【问题标题】:How to convince Idris in vector length如何在向量长度上说服 Idris
【发布时间】:2023-09-06 19:25:01
【问题描述】:

刚开始使用 Idris(如果重要的话,那就是 Idris 2),偶然发现了这个问题。我正在尝试实现一个函数,该函数返回一个由所有斐波那契数组成的向量,直到给定 n。这是到目前为止的定义(它不编译):

fibs : (n : Nat) -> Vect (n + 1) Int
fibs Z = [0]
fibs (S Z) = [0, 1]
fibs (S k) =
    case (fibs k) of
        (x :: y :: xs) => (x + y) :: x :: y :: xs

我得到的错误如下:

Can't solve constraint between:
    S (S ?len)
and
    plus ?_ (fromInteger 1)

错误指向表达式(x :: y :: xs)

我知道我需要向 Idris 证明 fibs k 的长度至少为 2,但我不明白如何做到这一点,以及为什么从现有定义中不明显。对我来说,fibs (S k) 中的 k 肯定必须 >= 1,否则 fibs Zfibs (S Z) 将匹配。 fibs k 的长度,其中k >= 1 因此必须>= 2,根据fibs : (n : Nat) -> Vect (n + 1) Int 的定义。我缺少什么以及如何在 Idris 中表达这一点?

【问题讨论】:

    标签: idris dependent-type


    【解决方案1】:

    第一个1 + nn + 1 更容易推断类型检查器,因为左侧的plus 模式匹配:

    > :printdef plus
    plus : Nat -> Nat -> Nat
    plus 0 right = right
    plus (S left) right = S (plus left right)
    

    所以在不知道n的情况下,类型定义中的n + 1不能归约,而1 + n可以归约为S n

    仅此fibs 编译。但是如果你想检查函数是否是总的,你会得到:

    > :total fibs
    Main.fibs is possibly not total due to:
    Main.case block in fibs at test.idr:7:10-17, which is not total as there are missing cases
    

    因为类型检查器不查看其他子句,所以它不知道fibs (S Z) 已经在其他地方处理。因此在fibs (S k) = case (fibs k) of … k 可以Z,然后结果是Vect (S Z),在case switch 中没有处理。

    但修复和第一个一样简单,只需在 S (S k) 上进行模式匹配:

    fibs (S (S k)) = case (fibs (S k)) of
        (x :: y :: xs) => (x + y) :: x :: y :: xs
    

    【讨论】:

    • 很好的答案,谢谢! 1 + n vs n + 1 真的让人大开眼界,如果有点不幸的话,因为对于像我这样的初学者来说很容易偶然发现它。我已经尝试过(S (S k)),但是没有1 + n,它也不起作用。
    • @Haspemulator 危险信号实际上是使用 any 函数作为数据类型的参数。您应该始终尝试在类型中坚持使用构造函数。我觉得写(n : Nat) -> Vect (1 + n) Nat很奇怪,会改成(n : Nat) -> Vect (S n) Nat,相当于。
    • @HTNW 感谢您的观点!将函数用作类型定义的一部分真的很诱人,因为 Idris 使它成为可能,这与我以前使用的所有其他语言相反。但也许这是一个很好的例子,“即使你可以,并不意味着你应该”,因为它推动类型检查器非常接近可能的边界。