3

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
Incerteza
  • 32,326
  • 47
  • 154
  • 261
  • possible duplicate of [turn off lazy evaluation in haskell](http://stackoverflow.com/questions/15627849/turn-off-lazy-evaluation-in-haskell) – senia Sep 30 '13 at 05:44
  • 9
    Who said function is evaluated again and again in haskell?? – Satvik Sep 30 '13 at 05:48
  • 1
    This is not evaluated "over and over again" – Don Stewart Sep 30 '13 at 13:11
  • 1
    This question is based on a number of misconceptions about the behavior of Haskell. – Rein Henrichs Sep 30 '13 at 15:13
  • 1
    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 Oct 01 '13 at 00:53
  • @jacobm, aaaaa, at least once, but not once. That's what I was looking for. – Incerteza Oct 01 '13 at 00:58
  • 1
    @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 Oct 01 '13 at 01:02

3 Answers3

19

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

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 1
    It's a constant. A constant is just a 0-ary pure function. – Jörg W Mittag Sep 30 '13 at 11:17
  • 3
    No, in Haskell a function has exactly one argument. If it doesn't have an argument, it's not a function. – augustss Sep 30 '13 at 15:16
  • 2
    @JörgWMittag Yes, I agree, and I'm fine with calling it a 0-ary function, especially when it would be part of a sequence of n-ary functions that naturally generalize to 0-ary-ness. (I'm a bit contrarian to popular opinion here.) But calling it a function without any qualifier (like 0-ary) and not clearly as part of such a sequence is very strange. – Daniel Wagner Sep 30 '13 at 19:14
  • 1
    @Alex As far as I can tell, nothing about your update changes my answer. – Daniel Wagner Oct 01 '13 at 00:46
7

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.

jacobm
  • 13,790
  • 1
  • 25
  • 27
2

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

Boris
  • 5,094
  • 4
  • 45
  • 71
  • 1
    `fib () = 0 : 1 : zipWith (+) fib (tail fib)` is broken. Perhaps you meant `fib () = 0 : 1 : zipWith (+) (fib ()) (tail (fib ()))`? But that doesn't memoize. `fib () = let x = 0 : 1 : zipWith (+) x (tail x) in x` is probably what you want, but the content of the let expression might get lifted by an unfriendly compiler which would cause unwanted sharing. – Philip JF Sep 30 '13 at 08:18
  • @PhilipJF: Doh. I shouldn't write code while still in the process of waking up :) Thanks for catching. – Boris Sep 30 '13 at 09:01
  • 1
    Did you test this? I suspect you will find that, with optimizations turned on, GHC is too clever for you: `x` will be floated out to the top level for you and calling the new `fib () !! 100000` will hog memory in exactly the same way as the old `fib !! 100000` did. See also [this question about how to properly prevent CAFs from sharing memory](http://stackoverflow.com/questions/6090932/how-to-make-a-caf-not-a-caf-in-haskell). – Daniel Wagner Sep 30 '13 at 10:02
  • @DanielWagner: I tested it, and it behaved as you said, of course. Thanks for leading me to look at Core for the first time, it was enlightening. – Boris Sep 30 '13 at 12:30
  • `val () = 1+2+3`. How do I call it? I can't just say print val, it doesn't work. Can you please take a look at my update2? – Incerteza Oct 01 '13 at 00:47