简体   繁体   English

斯卡拉在哈斯克尔的风格

[英]Scala's style val in Haskell

In Scala I can express a function which is evaluated each time I refer to it: 在Scala中,我可以表达一个函数,每次引用它时都会对它进行评估:

def function = 1 + 2 + 3

or a val which is evaluated only once 或仅评估一次的val

val val1 = 1 + 2 + 3

even if I call it many times. 即使我多次打电话。

In Haskell I can, of course, define a function (which, again, evaluates over and over again whenever I call it): 当然,在Haskell中,我可以定义一个函数(当我调用它时,它会反复评估):

function = 1 + 2 + 3

Can I define an analog of Scala's val in Haskell? 我可以在Haskell中定义Scala的val模拟吗?

UPDATE: The question is not about the lazy evaluation. 更新:问题不在于懒惰的评估。

UPDATE2: UPDATE2:

How many times the value of function will be evaluated? 将评估function值的次数?

function = 1 + 2 + 3
main = do
        print function
        print function
        print function

Haskell doesn't specify very many ways to control the number of times or order things are evaluated in. That said, GHC evaluates CAFs at most once (unless they are typeclass polymorphic). Haskell没有指定很多方法来控制次数或命令事物的评估。也就是说,GHC最多只评估一次CAF (除非它们是类型多态)。 Note that with the monomorphism restriction on (the default), function = 1 + 2 + 3 defines function to be a monomorphic CAF, so function is evaluated at most once. 注意,对于(默认)单态限制, function = 1 + 2 + 3function定义为单态CAF,因此function最多被评估一次。

You will also find many people object to calling this a function, since it is not one unless you have done some pretty exotic things. 您还会发现许多人反对将此称为函数,因为除非您做了一些非常奇特的事情,否则它不是一个函数。 (There is no arrow in its type.) (其类型中没有箭头。)

You can certainly write: 你当然可以写:

let x = 1 + 2 + 3 in ...

instead of 代替

let myFunc () = 1 + 2 + 3 in ...

but in Haskell it doesn't really make sense to think about how often either of those is evaluated, except that the bodies with both be evaluated at least once if they're needed for a computation and never if they're not. 但是在Haskell中,考虑其中任何一个的评估频率并没有多大意义,除了如果计算需要它们两者至少评估一次,从不评估它们。

The key difference between Haskell and Scala here is that in Scala or any other strict language, the binding form val x = 1 + 2 + 3 tells Scala to evaluate the expression 1 + 2 + 3 and bind its result to a variable. Haskell和Scala之间的关键区别在于,在Scala或任何其他严格语言中,绑定形式val x = 1 + 2 + 3告诉Scala评估表达式1 + 2 + 3并将其结果绑定到变量。 In Haskell on the other hand, let x = 1 + 2 + 3 bind the computation 1 + 2 + 3 to x and the question of when or even whether to evaluate the expression isn't decided by the let expression at all. 在Haskell,另一方面, let x = 1 + 2 + 3的绑定的计算1 + 2 + 3x和时,甚至是否计算表达式的问题是没有被决定let表达在所有。

Since Haskell binding forms don't get to decide the policy by which the expressions they're associated with get evaluated, there's no way to write an exact analogue of the Scala version. 由于Haskell绑定表单无法决定与其关联的表达式进行评估的策略,因此无法编写Scala版本的精确模拟。

EDIT: Disregard this answer. 编辑:忽略这个答案。 It only works when you compile without optimization. 它只适用于没有优化的编译。 Taking my fibonacci implementation as an example, ghc -O0 -ddump-simpl main.hs gives a single fib definition in Core: 以我的斐波纳契实现为例, ghc -O0 -ddump-simpl main.hs在Core中给出了一个单独的fib定义:

Main.fib :: forall a_agB. GHC.Num.Num a_agB => () -> [a_agB]

but compiling with O2 optimizations ghc -O2 -ddump-simpl main.hs implements the fibonacci list as a constant value at the top level 但是使用O2优化进行编译ghc -O2 -ddump-simpl main.hs将斐波纳契列表实现为顶层的常量值

Main.$wfib :: forall a_agG. GHC.Num.Num a_agG => [a_agG]

This then gets called by the enclosing fibonacci implementation (which does take a () argument) 然后由封闭的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 }

The result is, that while Main.fib doesn't get memoized, Main.$wfib will still be memoized and take up memory. 结果是,当Main.fib没有得到记忆时, Main.$wfib仍然会被记忆并占用内存。


Original answer below: 原答案如下:

As Daniel Wagner said in another answer, a definition such as this 正如丹尼尔瓦格纳在另一个答案中所说,这样的定义

val = 1+2+3

would only evaluate once, unless it was polymorphic. 只会评估一次,除非它是多态的。 If you want to evaluate it every time you refer to it, you could do 如果你想在每次引用它时进行评估,你就可以做到

val () = 1+2+3

This way you have to give the argument () to val , but then it won't save the computed value. 这样你就必须将参数()赋予val ,但是它不会保存计算值。 This can be a good thing, if the computed value of val takes a lot of memory, but is easy to compute, and is consumed incrementally when you need it. 如果val的计算值占用大量内存,但很容易计算,并且在需要时逐步消耗,这可能是一件好事。 For instance, you could have a list of the fibonacci numbers: 例如,您可以拥有斐波那契数字列表:

fib = 0 : 1 : zipWith (+) fib (tail fib)

If you now do fib !! 100000 如果你现在做的话fib !! 100000 fib !! 100000 , you will need to compute all the first 100000 fibonacci numbers. fib !! 100000 ,您需要计算所有前100,000个斐波纳契数。 The value fib refers to this list, so it can't be garbage collected, but will hang around in memory. fib表示此列表,因此它不能被垃圾收集,但会在内存中挂起。 To counter this, you could do 为了解决这个问题,你可以做到

fib () = let x = 0 : 1 : zipWith (+) x (tail x) in x

Since fib is now a (constant) function instead of a constant value, its value will not be saved, freeing up memory. 由于fib现在是(常量)函数而不是常量值,因此不会保存其值,从而释放内存。

Edit: As Philip JF said in the comments, the content of the let expression might get lifted by an unfriendly compiler which would cause unwanted sharing 编辑:正如Philip JF在评论中所说,let表达式的内容可能被一个不友好的编译器解除,这将导致不必要的共享

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM