I always thought that replacing an expression x :: ()
with () :: ()
would be one of the most basic optimizations during compiling Haskell programs. Since ()
has a single inhabitant, no matter what x
is, it's result is ()
. This optimization seemed to me as a significant consequence of referential transparency. And we could do such an optimization for any type with just a single inhabitant.
(Update: My reasoning in this matter comes from natural deduction rules. There the unit type corresponds to truth (⊤) and we have an expansion rule "if x : ⊤
then () : ⊤
". For example see this text p. 20. I assumed that it's safe to replace an expression with its expansion or contractum.)
One consequence of this optimization would be that undefined :: ()
would be replaced by () :: ()
, but I don't see this as a problem - it would simply make programs a bit lazier (and relying on undefined :: ()
is certainly a bad programming practice).
However today I realized that such optimization would break Control.Seq
completely. Strategy
is defined as
type Strategy a = a -> ()
and we have
-- | 'rseq' evaluates its argument to weak head normal form.
rseq :: Strategy a
rseq x = x `seq` ()
But rseq x :: ()
so the optimization would simply discard the required evaluation of x
to WHNF.
So where is the problem?
- Does the existence of
rseq
andseq
break referential transparency (even if we consider only terminating expressions)? - Or is this a flaw of the design of
Strategy
and we could devise a better way how to force expressions to WHNF compatible with such optimizations?