We should distinguish between the value of an expression, and its memory representation.
For instance, both these expressions have the same value:
e1 = ("Hello", "Hello")
e2 = let s = "Hello" in (s, s)
Indeed, in Haskell there is no way to distinguish between the results of the evaluation of the expressions above. They are semantically equivalent.
A Haskell implementation (e.g. GHC) is free to represent those values in memory in any way that does not break the semantics. For instance:
- It might store the string
"Hello" twice in memory, and then use a pair of pointers (p1, p2).
- It might store the string
"Hello" once in memory, and then use a pair of pointers (p, p).
Note that both representations could, in theory, be used for any one of the expressions e1,e2 above. In practice, GHC will use the former for e2 and the latter for e1, but that's not important.
In your trees, the same issue arises. The value of your a6 is a tree. GHC probably represents that tree as a non-tree DAG (i.e. a DAG which, if transformed into an undirected graph, has a cycle), but it does not matter, it's only an implementation detail. The important aspect is that such representation respects the semantics of a tree.
One might then wonder why a DAG representation is sound if the value is a tree. This holds because in Haskell we can not compare the underlying "references" p used by GHC. If we had a function comparePtr :: a -> a -> Bool which compares such references we could distinguish between e1 and e2 by using comparePtr (fst e) (snd e) for e between e1,e2. That would horribly break the soundness of the implementation. In Haskell we do not have that, though.
(Well, technically, there is an unsafe function doing that, but unsafe functions should never be used in "normal" code. We usually pretend those do not exist.)