Can GHC simplify id = (\(a, b) -> (a, b)).(\(a, b) -> (a, b))
into id = \(a, b) -> (a, b)
?
What about more complicated case :
id (Just x) = Just x
id Nothing = Nothing
map f (Just x) = Just (f x)
map _ Nothing = Nothing
Will GHC simplify id . map
into map
?
I tried to use plain beta reduction but it looks like these terms are irreducible because of the nasty pattern matching.
Therefore I am curious how do GHC's optimization techniques deal with that.
You can ask these questions of ghc by running it with -ddump-simpl
. This will cause ghc to dump the "core" code that it compiles programs into. Core is an intermediate language between the part of the compiler that reasons about Haskell code and the part of the compiler that transforms that code into machine code.
When I compiled the following with -O2 -ddump-simpl
the results surprised me.
tupid1 :: (a, b) -> (a, b)
tupid1 = (\(a, b) -> (a, b))
tupid2 :: (a, b) -> (a, b)
tupid2 = (\(a, b) -> (a, b)) . (\(a, b) -> (a, b))
The resulting core for tupid1
makes a new specialized identity function.
-- RHS size: {terms: 4, types: 7, coercions: 0}
tupid1 :: forall a_aqo b_aqp. (a_aqo, b_aqp) -> (a_aqo, b_aqp)
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=DmdType <S,1*U(U,U)>m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)
Tmpl= \ (@ a_ayd)
(@ b_aye)
(ds_dIl [Occ=Once] :: (a_ayd, b_aye)) ->
ds_dIl}]
tupid1 = \ (@ a_ayd) (@ b_aye) (ds_dIl :: (a_ayd, b_aye)) -> ds_dIl
In core the polymorphic type arguments to functions are represented as explicit arguments. tupid1
takes two of these type arguments, named a_ayd
and b_aye
, for the two type variables a
and b
in its signature. It also takes a term ds_dIl
that has the type of a tuple of those two types (ds_dIl :: (a_ayd, b_aye)
) and returns it unmodified.
The surprising result is tupid2
...
-- RHS size: {terms: 1, types: 0, coercions: 0}
tupid2 :: forall a_aqm b_aqn. (a_aqm, b_aqn) -> (a_aqm, b_aqn)
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=DmdType <S,1*U(U,U)>m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)
Tmpl= \ (@ a_axZ) (@ b_ay0) (x_aIw [Occ=Once] :: (a_axZ, b_ay0)) ->
x_aIw}]
tupid2 = tupid1
... which ghc simplifies to tupid1
! How it deduces that is beyond my immediate knowledge or ability to discover.
The identity example for Maybe
maybeid :: Maybe a -> Maybe a
maybeid (Just x) = Just x
maybeid Nothing = Nothing
Is also simplified to an identity function with no pattern matching
-- RHS size: {terms: 3, types: 4, coercions: 0}
maybeid :: forall a_aqn. Maybe a_aqn -> Maybe a_aqn
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=DmdType <S,1*U>,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)
Tmpl= \ (@ a_aqI) (ds_dIq [Occ=Once] :: Maybe a_aqI) -> ds_dIq}]
maybeid = \ (@ a_aqI) (ds_dIq :: Maybe a_aqI) -> ds_dIq
The core for map
for Maybe
isn't interesting for this question
maybemap :: (a -> b) -> Maybe a -> Maybe b
maybemap f (Just x) = Just (f x)
maybemap _ Nothing = Nothing
But if it's composed with maybeid
maybeidmap :: (a -> b) -> Maybe a -> Maybe b
maybeidmap f = maybeid . maybemap f
ghc simplifies it to maybemap
-- RHS size: {terms: 1, types: 0, coercions: 0}
maybeidmap
:: forall a_aqp b_aqq.
(a_aqp -> b_aqq) -> Maybe a_aqp -> Maybe b_aqq
[GblId,
Arity=2,
Caf=NoCafRefs,
Str=DmdType <L,1*C1(U)><S,1*U>,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)
Tmpl= maybemap}]
maybeidmap = maybemap
And it does the same thing if id
is composed with f
.
maybemapid :: (a -> b) -> Maybe a -> Maybe b
maybemapid f = maybemap (id . f)
The composition with the identity function is removed and the whole function simplifies to maybemap
-- RHS size: {terms: 1, types: 0, coercions: 0}
maybemapid
:: forall a_aqq b_aqr.
(a_aqq -> b_aqr) -> Maybe a_aqq -> Maybe b_aqr
[GblId,
Arity=2,
Caf=NoCafRefs,
Str=DmdType <L,1*C1(U)><S,1*U>,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=False)
Tmpl= \ (@ a_ar2)
(@ b_ar3)
(f_aqL [Occ=Once!] :: a_ar2 -> b_ar3)
(eta_B1 [Occ=Once!] :: Maybe a_ar2) ->
case eta_B1 of _ [Occ=Dead] {
Nothing -> GHC.Base.Nothing @ b_ar3;
Just x_aqJ [Occ=Once] -> GHC.Base.Just @ b_ar3 (f_aqL x_aqJ)
}}]
maybemapid = maybemap
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With