Here are a four different lists, where the first three are already evaluated.
The first one, l, was defined as a top level constant as
> l = [1,2,3]
and is now found at 0xaf305d90 (where the /2 is the pointer tag information) and fully evaluated:
> ConsClosure {info = StgInfoTable {ptrs = 2, nptrs = 0, tipe = CONSTR_2_0, srtlen = 1}, ptrArgs = [0xb2efaff4,0xaf2d4248], dataArgs = [], pkg = "ghc-prim", modl = "GHC.Types", name = ":"}
The second one, l2, is locally defined
> let l2 = 4:l
and now found at 0xaf305870. See how the cons-cell references l!
> ConsClosure {info = StgInfoTable {ptrs = 2, nptrs = 0, tipe = CONSTR_2_0, srtlen = 1}, ptrArgs = [0xb2efb00c,0xaf305d90], dataArgs = [], pkg = "ghc-prim", modl = "GHC.Types", name = ":"}
And the binding
> args
gives us at 0xb2f6920c/1 a static, but at compile time unknown list:
> ConsClosure {info = StgInfoTable {ptrs = 0, nptrs = 0, tipe = CONSTR_NOCAF_STATIC, srtlen = 0}, ptrArgs = [], dataArgs = [], pkg = "ghc-prim", modl = "GHC.Types", name = "[]"}
And now we have, at 0xaf305858, the concatenation of them, but unevaluated:
> let x = l ++ l2 ++ args
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:
> APClosure {info = StgInfoTable {ptrs = 0, nptrs = 0, tipe = AP, srtlen = 0}, arity = 21669, n_args = 2, fun = 0xaf305d74, payload = [0xaf305870,0xb2f6920c/1]}
Now to some more closure types. m and m' locally bound of type the unboxed type Int#, with values 42 resp. 23.
> let f = \x n -> take (I# m + I# x) n ++ args
> t = f m' l2
So here is (0xafd3be14), referencing its free variables args and 42:
> APClosure {info = StgInfoTable {ptrs = 0, nptrs = 0, tipe = AP, srtlen = 0}, arity = 57205, n_args = 2, fun = 0xaeff8360, payload = [0xb2f6920c/1]}
And t is a thunk that applies f (also referenced here) to an unboxed value (23) and l2:
> APClosure {info = StgInfoTable {ptrs = 0, nptrs = 0, tipe = AP, srtlen = 0}, arity = 58149, n_args = 3, fun = 0xaeff837c, payload = [0xafd3be14,0xaf305870]}
Lastly, here is the standard example for self reference:
> let x = id (:) () x
This is what x (0xb05d0108) looks like, at least without -O:
> APClosure {info = StgInfoTable {ptrs = 0, nptrs = 0, tipe = AP, srtlen = 0}, arity = 216, n_args = 1, fun = 0xaeff7084, payload = [0xb05d0108]}
So it is unevaluated. Let us evaluate it using seq. Now we have, still at 0xb05d0108:
> BlackholeClosure {info = StgInfoTable {ptrs = 1, nptrs = 0, tipe = BLACKHOLE, srtlen = 0}, indirectee = 0xb0417d20/2}
The thunk was replaced by an indirection. If we look at the target, 0xb0417d20/2, we see that it is a newly created cons-cell referencing the original location of x:
> ConsClosure {info = StgInfoTable {ptrs = 2, nptrs = 0, tipe = CONSTR_2_0, srtlen = 1}, ptrArgs = [0xb2f690f4,0xb05d0108], dataArgs = [], pkg = "ghc-prim", modl = "GHC.Types", name = ":"}
After running the garbage collector (performGC), we find that the address of x is now 0xb0056094/2 and that the self-reference is without indirections:
> ConsClosure {info = StgInfoTable {ptrs = 2, nptrs = 0, tipe = CONSTR_2_0, srtlen = 1}, ptrArgs = [0xb2f690f4,0xb0056094/2], dataArgs = [], pkg = "ghc-prim", modl = "GHC.Types", name = ":"}