Search code examples
haskellghcghci

Does -XStrict do anything in GHC?


I thought -XStrict was supposed turn GHC into a Strict Haskell, so I tried the infinite Fibonacci sequence test

my_zipWith f  x [] = []
my_zipWith f []  y = []
my_zipWith f (x:xt) (y:yt) = f x y : my_zipWith f xt yt

test_fibs n = 
    let fibs = 0 : 1 : my_zipWith (+) fibs (tail fibs) in
    take n fibs

main = do
        putStr $ show $ test_fibs 15

to see if it would blow up in memory, but it doesn't:

$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.0.2

$ ghc -XStrict fibs.hs && ./fibs 
[1 of 1] Compiling Main             ( fibs.hs, fibs.o )
Linking fibs ...
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]

What am I doing wrong?


Solution

  • Strict pragma certainly let GHC evaluate everything strictly, but into only Weak Head Normal Form. For example:

    (a, b) = (error "a", error "b")
    

    If above code exists under Strict pragma, any error arises. Let's see your code:

    test_fibs n = 
        let fibs = 0 : 1 : my_zipWith (+) fibs (tail fibs) in
        take n fibs
    

    fibs called recursively but in list cons, so now whole list is WHNF. That is reason why your code didn't stack.

    This post will help you also. Enjoy recursion and laziness!

    Add:

    An easy way, to use deepseq:

    {-# LANGUAGE Strict #-}
    import Control.DeepSeq
    
    my_zipWith f  x [] = []
    my_zipWith f []  y = []
    my_zipWith f (x:xt) (y:yt) = force $ f x y : my_zipWith f xt yt
    
    test_fibs :: Int -> [Int]
    test_fibs n = 
        let fibs = 0 : 1 : my_zipWith (+) fibs (tail fibs) in
        force $ take n fibs
    
    main = do
            putStr $ show $ test_fibs 15
    

    force defined as force x = x `deepSeq` x, deepSeq evaluate an expression deeply literally to NF (Normal Form). This conversion is achieved with GHC.Generics. If convert manually, just have to evaluate inside of data, so can rewrite following:

    {-# LANGUAGE Strict #-}
    
    my_zipWith f  x [] = []
    my_zipWith f []  y = []
    my_zipWith f (x:xt) (y:yt) = f x y : go
        where
            go = my_zipWith f xt yt
    
    test_fibs n = 
        let
            fib2 = my_zipWith (+) fibs (tail fibs)
            fibs = 0 : 1 : fib2
        in
            take n fibs
    
    main = do
            putStr $ show $ test_fibs 15
    

    But they can't stack actually. Because GHC can detect infinite loop, but it's another story.