# LANGUAGE ExistentialQuantification #

import GHC.AssertNF
import Control.DeepSeq
import Data.Map.Strict

  1 ) simple container

data Obj a = Obj a

-- using a smart constructor here to ensure arbitrary values are strict
mkObj :: a -> Obj a
mkObj a = Obj $! a

  using a special  constructor to ensure Strings are always
-- fully evaluated in this example
mkString :: String -> String
mkString x = force x

xs :: Map Int (Obj String)
xs = fromList [ (1, mkObj . mkString $ "abc")
              , (2, mkObj . mkString $ "def")
              , (3, mkObj . mkString $ "hij")
              ]

  2 ) container using existential quantification

data Obj2 = forall a. (Show a) => Obj2 a

-- using the smart constructor here has no effect on strictness
mkObj2 :: Show a => a -> Obj2
mkObj2 a = Obj2 $! a

xs2 :: Map Int Obj2
xs2 = fromList [ (1, mkObj2 1)
               , (2, mkObj2 . mkString $ "test")
               , (3, mkObj2 'c')
               ]

  3 ) container using existential quantification and deepseq

data Obj3 = forall a. (NFData a, Show a) => Obj3 !a

instance NFData Obj3 where
  -- using default implementation

  even forcing full evaluation with deepseq has no effect on strictness
mkObj3 :: (NFData a, Show a) => a -> Obj3
mkObj3 a = Obj3 $!! a

xs3 :: Map Int Obj3
xs3 = fromList [ (1, mkObj3 (1::Int))
               , (2, mkObj3 . mkString $ "abc")
               , (3, mkObj3 ('c'::Char))
               ]

-- strictness tests
main :: IO ()
main = do
  putStr "test: simple container: "
  (isNF $! xs) >>= putStrLn . show
  assertNF $! xs
  putStr "test: heterogeneous container: "
  (isNF $! xs2) >>= putStrLn . show
  assertNF $! xs2
  putStr "test: heterogeneous container with NFData: "
  (isNF $!! xs3) >>= putStrLn . show
  assertNF $!! xs3
  return ()

 using a smart constructor here to ensure arbitrary values are strict
 fully evaluated in this example
 using the smart constructor here has no effect on strictness
 using default implementation
 strictness tests
# LANGUAGE ExistentialQuantification #

import GHC.AssertNF
import Control.DeepSeq
import Data.Map.Strict

  1 ) simple container

data Obj a = Obj a

mkObj :: a -> Obj a
mkObj a = Obj $! a

  using a special  constructor to ensure Strings are always
mkString :: String -> String
mkString x = force x

xs :: Map Int (Obj String)
xs = fromList [ (1, mkObj . mkString $ "abc")
              , (2, mkObj . mkString $ "def")
              , (3, mkObj . mkString $ "hij")
              ]

  2 ) container using existential quantification

data Obj2 = forall a. (Show a) => Obj2 a

mkObj2 :: Show a => a -> Obj2
mkObj2 a = Obj2 $! a

xs2 :: Map Int Obj2
xs2 = fromList [ (1, mkObj2 1)
               , (2, mkObj2 . mkString $ "test")
               , (3, mkObj2 'c')
               ]

  3 ) container using existential quantification and deepseq

data Obj3 = forall a. (NFData a, Show a) => Obj3 !a

instance NFData Obj3 where

  even forcing full evaluation with deepseq has no effect on strictness
mkObj3 :: (NFData a, Show a) => a -> Obj3
mkObj3 a = Obj3 $!! a

xs3 :: Map Int Obj3
xs3 = fromList [ (1, mkObj3 (1::Int))
               , (2, mkObj3 . mkString $ "abc")
               , (3, mkObj3 ('c'::Char))
               ]

main :: IO ()
main = do
  putStr "test: simple container: "
  (isNF $! xs) >>= putStrLn . show
  assertNF $! xs
  putStr "test: heterogeneous container: "
  (isNF $! xs2) >>= putStrLn . show
  assertNF $! xs2
  putStr "test: heterogeneous container with NFData: "
  (isNF $!! xs3) >>= putStrLn . show
  assertNF $!! xs3
  return ()
