How is Haskell's seq used?
Asked Answered
P

1

8

So, Haskell seq function forces the evaluation of it's first argument and returns the second. Consequently it is an infix operator. If you want to force the evaluation of an expression, intuitively such a feature would be a unary operator. So, instead of

seq :: a -> b -> b

it would be

seq :: a -> a

Consequently, if the value you want is a, why return b and how do you construct for the return of b. Clearly, I am not thinking Haskell. :)

Phosphorus answered 22/9, 2016 at 17:19 Comment(1)
If you look at the (very poorly formatted) definition of foldl', you see that you often just want to strictly evaluate some let-bound variable in a that you feed into your b value.Smokechaser
S
19

The way to think about a `seq` b is not that it "evaluates a" but that it creates a dependency between a and b, so that when you go to evaluate b you evaluate a as well.

This means, for example, that a `seq` a is completely redundant: you're telling Haskell to evaluate a when you evaluate a. By the same logic, seq a with just one argument would not be any different than simply writing a by itself.

Just having seq a that somehow evaluates a would not work. The problem is that seq a is itself an expression that might not be evaluated—it might be deep inside some nested thunks, for example. So it would only become relevant when you get to evaluating the whole seq a expression—at which point you would have been evaluating a by itself anyhow.

@Rhymoid's example of how it's used in a strict fold (foldl') is good. Our goal is to write a fold such that its intermediate accumulated value (acc) is completely evaluated at each step as soon as we evaluate the final result. This is done by adding a seq between the accumulated value and the recursive call:

foldl' f z (x:xs) = 
  let z' = f z x in z' `seq` foldl' f z' xs

You can visualize this as a long chain of seq between each application of f in the fold, connecting all of them to the final result. This way when you evaluate the final expression (ie the number you get by by summing a list), it evaluates the intermediate values (ie partial sums as you fold through the list) strictly.

Successor answered 22/9, 2016 at 17:23 Comment(3)
I am now thinking the dependency created by seq is redundant at least in the case of foldl as described above since the regular foldl already impose such a dependency constraint on z' by virtue of using it...it seems.Phosphorus
@Phosphorus foldl (\x y -> 1) undefined [2] evaluates to 1, instead of trying to evaluate the undefined. By comparison, using foldl' errors out, since it does evaluate it.Preachment
@Phosphorus One of the characteristics you are used to in other languages that does not apply to Haskell is this: a function application does not obviously "use" all the arguments in the application. In particular, just because the application foldl' f z' xs has z' as an argument does not necessarily (i.e. without inspecting the definition of foldl') mean that z' gets used. This is where your argument about z' being used falls down: z' is an argument to a function, hence not necessarily used.Undersea

© 2022 - 2024 — McMap. All rights reserved.