I'm currently playing around a bit with the possibility of removing a level of indirection from all lifted but newtype-like types (single constructor with a single argument) in Haste; for instance, Int
would get a plain number
as its runtime representation (i.e. 42
), rather than an array containing a constructor tag and a number as is currently done (i.e. [0,42]
).
However, GHC seems to be optimizing conversions between types that have an identical runtime representation, which breaks this scheme. Consider the following:
data Integer = Small Int# | Big ExternalGMPStuff
data Int = I# Int#
convert :: Int -> Integer
convert (I# i) = Small i
This is how the Integer
and Int
types are defined with GHC. Now, Small
and I#
will end up having the same constructor tag, which means that for small values, Integer
and Int
will have the same runtime representation. The convert
function may thus reasonably be optimized away and indeed it seems like GHC is doing just that, in some cases.
When the runtime representation of Int
changes to be the same as that of Int#
, this optimization is obviously not valid anymore. Since being able to remove a level of indirection from all types that do not need it would be a rather big win, I would very much like to know:
a) In what circumstances does GHC perform this kind of optimization (triggering this is not very straightforward, and I can't find any rewrite rules in base
or integer-gmp
that explicitly do this); and
b) is there any way to make it stop without having to disable optimizations altogether?