对 Idris 中的惰性求值感到困惑

confused about lazy evaluation in Idris

阅读和尝试官方 Idris 教程中的一些示例让我对惰性求值有点困惑。

正如教程中所述,Idris 使用急切求值,他们继续给出了一个不合适的示例

ifThenElse : Bool -> a -> a -> a
ifThenElse True  t e = t
ifThenElse False t e = e

然后他们继续展示使用惰性求值的示例

ifThenElse : Bool -> Lazy a -> Lazy a -> a
ifThenElse True  t e = t
ifThenElse False t e = e

我喜欢边看书边尝试,所以我创建了一个低效的斐波那契函数来测试非懒惰和懒惰ifThenElse

fibo : Nat -> Nat
fibo Z = Z
fibo (S Z) = S Z
fibo (S(S Z)) = S(S Z)
fibo (S(S(S n))) = fibo (S(S n)) + fibo (S n)

-- the non lazy
ifThenElse1 : Bool -> (t: a) -> (f: a) -> a
ifThenElse1 False t f = f
ifThenElse1 True t f = t

-- should be slow when applied to True
iftest1 : Bool -> Nat
iftest1 b = ifThenElse1 b (fibo 5) (fibo 25)

-- the lazy
ifThenElse2 : Bool -> (t: Lazy a) -> (f: Lazy a) -> a
ifThenElse2 False t f = f
ifThenElse2 True t f = t

-- should be fast when applied to True
iftest2 : Bool -> Nat
iftest2 b = ifThenElse2 b (fibo 5) (fibo 25)

鉴于 Idris 应该执行急切的评估,我预计 iftest1 的执行速度会减慢 (fibo 25),即使应用于 True。但是,iftest1iftest2 在应用于 True 时执行得非常快。所以也许我对 lazyness/eagerness 的理解存在根本性的缺陷?

观察 Idris 中懒惰和急切之间区别的一个很好的例子是什么?

您可能尝试了 Idris REPL 中的 iftest1iftest2The REPL uses different evaluation order than compiled code:

Being a fully dependently typed language, Idris has two phases where it evaluates things, compile-time and run-time. At compile-time it will only evaluate things which it knows to be total (i.e. terminating and covering all possible inputs) in order to keep type checking decidable. The compile-time evaluator is part of the Idris kernel, and is implemented in Haskell using a HOAS (higher order abstract syntax) style representation of values. Since everything is known to have a normal form here, the evaluation strategy doesn’t actually matter because either way it will get the same answer, and in practice it will do whatever the Haskell run-time system chooses to do.

The REPL, for convenience, uses the compile-time notion of evaluation.