[英]Scala's style val in Haskell
在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
Haskell没有指定很多方法来控制次数或命令事物的评估。也就是说,GHC最多只评估一次CAF (除非它们是类型多态)。 注意,对于(默认)单态限制, function = 1 + 2 + 3
将function
定义为单态CAF,因此function
最多被评估一次。
您还会发现许多人反对将此称为函数,因为除非您做了一些非常奇特的事情,否则它不是一个函数。 (其类型中没有箭头。)
你当然可以写:
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版本的精确模拟。
编辑:忽略这个答案。 它只适用于没有优化的编译。 以我的斐波纳契实现为例, 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]
然后由封闭的fibonacci实现调用它(它确实采用a ()
参数)
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
仍然会被记忆并占用内存。
原答案如下:
正如丹尼尔瓦格纳在另一个答案中所说,这样的定义
val = 1+2+3
只会评估一次,除非它是多态的。 如果你想在每次引用它时进行评估,你就可以做到
val () = 1+2+3
这样你就必须将参数()
赋予val
,但是它不会保存计算值。 如果val
的计算值占用大量内存,但很容易计算,并且在需要时逐步消耗,这可能是一件好事。 例如,您可以拥有斐波那契数字列表:
fib = 0 : 1 : zipWith (+) fib (tail fib)
如果你现在做的话fib !! 100000
fib !! 100000
,您需要计算所有前100,000个斐波纳契数。 值fib
表示此列表,因此它不能被垃圾收集,但会在内存中挂起。 为了解决这个问题,你可以做到
fib () = let x = 0 : 1 : zipWith (+) x (tail x) in x
由于fib
现在是(常量)函数而不是常量值,因此不会保存其值,从而释放内存。
编辑:正如Philip JF在评论中所说,let表达式的内容可能被一个不友好的编译器解除,这将导致不必要的共享
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.