Understanding Haskell seq

I am new to Haskell, I have problems understanding seq , which I have summarized in the example below.

Here are 2 implementations of the same (stupid) function. The function takes a positive int n and returns a tuple (n,n) . The answer is created by counting from (0,0) using an auxiliary function with the battery of tuples. To avoid building up large volumes, I used seq to make the battery more stringent.

In testSeq1 contents of the tuple are strict, while in testSeq2 the tuple itself is strict. I thought these 2 implementations would execute the same way. But in fact, the “shared memory in use” is only 1 MB for testSeq1 , but massive 187 MB for testSeq2 (when testing using n = 1,000,000).

What happened to testSeq2 ?

 testSeq1 :: Int -> (Int,Int) testSeq1 n = impl n (0,0) where impl 0 (a,b) = (a,b) impl i (a,b) = seq a $ seq b $ impl (i-1) (a+1, b+1) testSeq2 :: Int -> (Int,Int) testSeq2 n = impl n (0,0) where impl 0 acc = acc impl i acc = seq acc $ impl (i-1) ((fst acc)+1, (snd acc)+1) 
+4
source share
2 answers

seq only slightly forces evaluation of its first argument. You can see these two examples:

 errorTuple :: (Int, Int) errorTuple = undefined errorTupleContents :: (Int, Int) errorTupleContents = (undefined, undefined) case1 = errorTuple `seq` (1, 1) case2 = errorTupleContents `seq` (1, 1) 

case1 will fail with an undefined error because seq tries to force an errorTuple estimate that is undefined , but case2 will not, because the tuple constructor is evaluated and returns a tuple whose contents are not evaluated. If they were rated, they would be undefined .

+5
source

seq A tuple only makes it evaluate as much as its tuple constructor exposes, but will not evaluate its components.

I.e

 let pair = id (1+1, 2+2) in seq pair $ ... 

will apply id and produce (_thunk1, _thunk2) , where _thunk indicates additions that are not currently being evaluated.

In your second example, you force the acc battery, but not its components, so some big tricks still accumulate.

You can use the so-called evaluation strategy, for example:

 evalPair :: (a,b) -> () evalPair (a,b) = seq a $ seq b () testSeq2 :: Int -> (Int,Int) testSeq2 n = impl n (0,0) where impl 0 acc = acc impl i acc = seq (evalPair acc) $ impl (i-1) ((fst acc)+1, (snd acc)+1) 

But then testSeq1 is a simpler approach.

Use strict tuples as another alternative. Such tuples never had a rumble for components, but only the results were evaluated. The forced tuple constructor will also force components as expected.

 import Data.Strict.Tuple testSeq2 :: Int -> Pair Int Int testSeq2 n = impl n (0 :!: 0) where impl 0 acc = acc impl i acc = seq acc $ impl (i-1) ((fst acc + 1) :!: (snd acc + 1)) 
+7
source

All Articles