Question

Here is a classic first attempt at a custom length function:

length1    []  = 0
length1 (x:xs) = 1 + length1 xs

And here is a tail-recursive version:

length2 = length2' 0 where
    length2' n    []  = n
    length2' n (x:xs) = length2' (n+1) xs

However, (n+1) will not be evaluted strictly, but instad Haskell will create a thunk, right?

Is this a correct way to prevent the creation of the thunk, thus forcing strict evaluation of (n+1)?

length3 = length3' 0 where
    length3' n    []  = n
    length3' n (x:xs) = length3' (n+1) $! xs

How would I achieve the same effect with seq instead of $!?

Was it helpful?

Solution

The usual way now to write it would be as:

length3 :: [a] -> Int
length3 = go 0
    where
        go :: Int -> [a] -> Int
        go n []     = n
        go n (x:xs) = n `seq` go (n+1) xs

Namely, as a fold over the list strict in the accumulator. GHC yields the direct translation to Core:

Main.$wgo :: forall a_abz. GHC.Prim.Int# -> [a_abz] -> GHC.Prim.Int#
Main.$wgo =
  \ (n :: GHC.Prim.Int#) (xs :: [a_abz]) ->
    case xs of
      [] -> n
      _ : xs -> Main.$wgo a (GHC.Prim.+# n 1) xs

Showing that it is unboxed (and thus strict) in the accumulator.

OTHER TIPS

I don't think that's quite right--$! is strict in its second argument, so you're just forcing the list and not the accumulator.

You can get the right strictness using seq something like this:

let n' = n + 1 in n' `seq` length3' n' xs

I think a more readable version would use bang patterns (a GHC extension):

length3' !n (x:xs) = ...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top