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 $!
?
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.