GHC doesn't optimize (strict) composition with id
Newtype constructors and selectors have no runtime overhead, but some uses of them do. For example, given newtype Identity a = Identity { runIdentity :: a }
, Identity
turns into id
, but Identity . f
turns into id . f
, which is distinct from f
, because it gets eta-expanded to \x -> f x
.
It would be nice to be able to compose a newtype constructor with a function without any overhead. The obvious thing to try is strict composition:
(#) :: (b -> c) -> (a -> b) -> a -> c
(#) f g = f `seq` g `seq` \x -> f (g x)
In theory this should get rid of the eta-expansion. In practice, the generated Core looks like this:
foo :: (a -> b) -> [a] -> [b]
foo f = map (id # f)
-- becomes
foo = \f e -> map (case f of g { __DEFAULT -> \x -> g x }) e
Different variations of (#)
, and turning -fpedantic-bottoms
on, don't seem to affect this. A simpler version, foo f = map (f
seq \x -> f x)
, generates the same sort of Core.
In one library we resorted to defining a bunch of functions of the form identityDot :: (a -> b) -> a -> Identity b; identityDot = unsafeCoerce
. It would be better to be able to rely on GHC to do the optimization directly, if we use strict composition anyway.
Trac metadata
Trac field | Value |
---|---|
Version | 7.6.1 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |