{-# LANGUAGE MagicHash, BangPatterns #-} import GHC.Exts import GHC.HeapView import Control.DeepSeq import System.Environment import System.Mem l = [1,2,3] main = do args <- map length `fmap` getArgs let l2 = 4:l (l ++ l2 ++ args) `deepseq` return () let x = l ++ l2 ++ args performGC putStrLn "ghc-heap-view-demo" putStrLn "" putStrLn "Here are a four different lists, where the first three are already evaluated." putStrLn "The first one, l, was defined as a top level constant as " putStrLn "> l = [1,2,3]" putStrLn $ "and is now found at " ++ show (asBox l) ++ " (where the /2 is the pointer tag information) and fully evaluated:" getClosureData l >>= printInd putStrLn $ "The second one, l2, is locally defined" putStrLn "> let l2 = 4:l" putStrLn $ "and now found at " ++ show (asBox l2) ++ ". See how the cons-cell references l!" getClosureData l2 >>= printInd putStrLn "And the binding" putStrLn "> args <- map length `fmap` getArgs" putStrLn $ "gives us at " ++ show (asBox args) ++ " a static, but at compile time unknown list:" getClosureData args >>= printInd getClosureData [] >>= printInd putStrLn $ "And now we have, at " ++ show (asBox x) ++ ", the concatenation of them, but unevaluated:" putStrLn "> let x = l ++ l2 ++ args" putStrLn "The thunk keeps a reference to l2 and args, but not l, as that is at a static address, unless you are running this in GHCi:" getClosureData x >>= printInd putStrLn "" putStrLn "Now to some more closure types. m and m' locally bound of type the unboxed type Int#, with values 42 resp. 23." putStrLn "> let f = \\x n -> take (I# m + I# x) n ++ args" putStrLn " t = f m' l2" let !(I# m) = length args + 42 let !(I# m') = length args + 23 let f = \x n -> take (I# m + I# x) n ++ args t = f m' l2 putStrLn $ "So here is (" ++ show (asBox f) ++ "), referencing its free variables args and 42:" getClosureData f >>= printInd putStrLn "And t is a thunk that applies f (also referenced here) to an unboxed value (23) and l2:" getClosureData t >>= printInd putStrLn "" putStrLn "Lastly, here is the standard example for self reference:" putStrLn "> let x = id (:) () x" let x = id (:) () x putStrLn $ "This is what x (" ++ show (asBox x) ++ ") looks like, at least without -O:" getClosureData x >>= printInd x `seq` return () putStrLn $ "So it is unevaluated. Let us evaluate it using seq. Now we have, still at " ++ show (asBox x) ++ ":" getClosureData x >>= printInd IndClosure {indirectee = target} <- getClosureData x putStrLn $ "The thunk was replaced by an indirection. If we look at the target, " ++ show target ++ ", we see that it is a newly created cons-cell referencing the original location of x:" getBoxedClosureData target >>= printInd performGC putStrLn $ "After running the garbage collector (performGC), we find that the address of x is now " ++ show (asBox x) ++ " and that the self-reference is without indirections:" getClosureData x >>= printInd printInd :: Show a => a -> IO () printInd x = putStrLn $ " " ++ show x recurse :: Int -> Box -> IO () recurse m = go 0 where go i b = if i >= m then return () else do putStrLn $ ind ++ show b c <- getBoxedClosureData b putStrLn $ ind ++ show c mapM_ (go (succ i)) (allPtrs c) where ind = concat $ replicate i " "