Ticket #7542 (new bug)
GHC doesn't optimize (strict) composition with id
| Reported by: | shachaf | Owned by: | simonpj |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.8.1 |
| Component: | Compiler | Version: | 7.6.1 |
| Keywords: | Cc: | ekmett@…, jwlato@…, hackage.haskell.org@…, pho@… | |
| Operating System: | Unknown/Multiple | Architecture: | Unknown/Multiple |
| Type of failure: | Runtime performance bug | Difficulty: | Unknown |
| Test Case: | Blocked By: | ||
| Blocking: | Related Tickets: |
Description
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.
