3
votes

In Scala I can express a function which is evaluated each time I refer to it:

def function = 1 + 2 + 3

or a val which is evaluated only once

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):

function = 1 + 2 + 3

Can I define an analog of Scala's val in Haskell?

UPDATE: The question is not about the lazy evaluation.

UPDATE2:

How many times the value of function will be evaluated?

function = 1 + 2 + 3
main = do
        print function
        print function
        print function
3
Who said function is evaluated again and again in haskell?? - Satvik
This is not evaluated "over and over again" - Don Stewart
This question is based on a number of misconceptions about the behavior of Haskell. - Rein Henrichs
The answer to your update 2 is: "At least once." You seem to be hung up on a particular unusual quirk of Haskell: by design, there is no way to tell how many times something gets evaluated, and it's not especially fruitful to try to think about Haskell code that way. In a language like Scala, it could be important to know how many times the expression associated with function gets evaluated, e.g. because it prints something; in Haskell, there is no way to tell, so just assume the compiler will try to evaluate it in the most efficient manner possible and think about other things. - jacobm
@jacobm I think it makes sense to draw a distinction between Haskell the spec and typical implementations. As you say, Haskell the spec says almost nothing, meaning "at least once" is the best you can say. But if you start talking about a specific implementation -- e.g. GHC -- then you can say a bit more, and in this case the something more is you can also say "at most once". - Daniel Wagner

3 Answers

19
votes

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). 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.

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.)

7
votes

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.

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. 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.

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.

2
votes

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:

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

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)

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.


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. 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. For instance, you could have a list of the fibonacci numbers:

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

If you now do fib !! 100000, you will need to compute all the first 100000 fibonacci numbers. The value fib refers to this list, so it can't be garbage collected, but will hang around in memory. 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.

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