Referential transparency is about equality statements and variable references. When you say x = y and your language is referentially transparent, then you can replace every occurence of x by y (modulo scope).
If you haven't specified x = ()
, then you can't replace x
by ()
safely, such as in your case. This is because you are wrong about the inhabitants of ()
, because in Haskell there are two of them: One is the only constructor of ()
, namely ()
. The other is the value that is never calculated. You may call it bottom or undefined:
x :: ()
x = x
You certainly can't replace any occurrence of x
by ()
here, because that would chance semantics. The existence of bottom in the language semantics allows some awkward edge cases, especially when you have a seq
combinator, where you can even prove every monad wrong. That's why for many formal discussions we disregard the existence of bottom.
However, referential transparency does not suffer from that. Haskell is still referentially transparent and as such a purely functional language.