在Scala中,我可以表达一个每次引用它时都要评估的函数:
def function = 1 + 2 + 3
或仅评估一次的val
val val1 = 1 + 2 + 3
即使我多次打电话也是如此。
在Haskell中,我当然可以定义一个函数(当我调用它时,它会反复评估):
function = 1 + 2 + 3
我可以在Haskell中定义Scala的val
模拟吗?
更新:问题不在于懒惰评估。
UPDATE2:
评估function
的价值的次数是多少次?
function = 1 + 2 + 3
main = do
print function
print function
print function
答案 0 :(得分:19)
Haskell没有指定很多方法来控制次数或命令事物的评估。也就是说,GHC最多评估一次CAFs(除非它们是类型多态)。请注意,对于单一性限制(默认值),function = 1 + 2 + 3
将function
定义为单态CAF,因此function
最多只评估一次。
你也会发现很多人都反对把它称为一个函数,因为它不是一个函数,除非你做了一些非常奇特的事情。 (其类型中没有箭头。)
答案 1 :(得分:7)
你当然可以写:
let x = 1 + 2 + 3 in ...
而不是
let myFunc () = 1 + 2 + 3 in ...
但是在Haskell中,考虑这些中的任何一个的评估频率并没有多大意义,除了如果计算需要它们两者至少进行一次评估,并且从不进行计算。
Haskell和Scala之间的主要区别在于,在Scala或任何其他严格语言中,绑定形式val x = 1 + 2 + 3
告诉Scala评估表达式1 + 2 + 3
并将其结果绑定到变量。另一方面,在Haskell中,let x = 1 + 2 + 3
将计算1 + 2 + 3
绑定到x
,并且何时或甚至是否来评估表达式的问题不是由let
表达式。
由于Haskell绑定表单无法决定与其关联的表达式所依据的策略,因此无法编写Scala版本的精确模拟。
答案 2 :(得分:2)
ghc -O0 -ddump-simpl main.hs
在Core中给出了一个单独的fib定义:
Main.fib :: forall a_agB. GHC.Num.Num a_agB => () -> [a_agB]
但使用O2
优化ghc -O2 -ddump-simpl main.hs
进行编译会将斐波纳契列表实现为顶级的常量值
Main.$wfib :: forall a_agG. GHC.Num.Num a_agG => [a_agG]
然后由封闭的斐波纳契实现调用(它确实采用()
参数)
Main.fib =
\ (@ a_agG) (w_stM :: GHC.Num.Num a_agG) (w1_stN :: ()) ->
case w1_stN of _ { () -> Main.$wfib @ a_agG w_stM }
结果是,虽然Main.fib
没有得到记忆,Main.$wfib
仍然会被记忆并占用记忆。
以下原始答案:
正如Daniel Wagner在另一个答案中所说的那样,这个定义
val = 1+2+3
只评估一次,除非它是多态的。如果你想在每次引用它时进行评估,你可以做
val () = 1+2+3
这样你必须将参数()
赋予val
,但它不会保存计算值。如果val
的计算值占用大量内存,但很容易计算,并且在需要时逐步消耗,这可能是一件好事。例如,您可以列出斐波那契数字:
fib = 0 : 1 : zipWith (+) fib (tail fib)
如果您现在fib !! 100000
,则需要计算所有前100,000个斐波纳契数。值fib
引用此列表,因此不能进行垃圾回收,但会在内存中挂起。为了解决这个问题,你可以做到
fib () = let x = 0 : 1 : zipWith (+) x (tail x) in x
由于fib
现在是(常量)函数而不是常量值,因此不会保存其值,从而释放内存。
编辑:正如Philip JF在评论中所说,let表达式的内容可能被不友好的编译器解除,这会导致不必要的共享