Unnecessary thunking of Integer operations
While playing around with code from this great post by @ekmett in ghc-vis
I noticed some thunks that I didn’t expect.
Consider this code:
{-# LANGUAGE BangPatterns #-}
module Memo where
data Tree a = Tree (Tree a) a (Tree a)
nats :: Tree Integer
nats = go 0 1
where
go !n !s = Tree (go l s') n (go r s')
where
l = n + s
r = l + s
s' = s * 2
I would expect the l
, r
, and s'
bindings to be evaluated eagerly. But this is the core I get:
Rec {
-- RHS size: {terms: 11, types: 17, coercions: 0, joins: 0/0}
go_r1Vm :: Integer -> Integer -> Tree Integer
[GblId, Arity=2, Str=<S,1*U><S,1*U>, Cpr=m1, Unf=OtherCon []]
go_r1Vm
= \ (w_s1Sf :: Integer) (w1_s1Sg :: Integer) ->
case Memo.$wgo w_s1Sf w1_s1Sg of
{ (# ww1_s1Su, ww2_s1Sv, ww3_s1Sw #) ->
Memo.Tree @Integer ww1_s1Su ww2_s1Sv ww3_s1Sw
}
-- RHS size: {terms: 32, types: 29, coercions: 0, joins: 0/2}
Memo.$wgo [InlPrag=NOUSERINLINE[2], Occ=LoopBreaker]
:: Integer -> Integer -> (# Tree Integer, Integer, Tree Integer #)
[GblId, Arity=2, Str=<S,1*U><S,1*U>, Unf=OtherCon []]
Memo.$wgo
= \ (w_s1Sf :: Integer) (w1_s1Sg :: Integer) ->
case w_s1Sf of n_X0 { __DEFAULT ->
case w1_s1Sg of s_X1 { __DEFAULT ->
let {
s'_s1PN :: Integer
[LclId]
s'_s1PN = GHC.Num.Integer.integerMul s_X1 Memo.f3 } in
let {
l_s1PM :: Integer
[LclId]
l_s1PM = GHC.Num.Integer.integerAdd n_X0 s_X1 } in
(# go_r1Vm l_s1PM s'_s1PN, n_X0,
case Memo.$wgo (GHC.Num.Integer.integerAdd l_s1PM s_X1) s'_s1PN of
{ (# ww1_X2, ww2_X3, ww3_X4 #) ->
Memo.Tree @Integer ww1_X2 ww2_X3 ww3_X4
} #)
}
}
end Rec }
Note the lazy let
bindings!
(Also note the relatively pointless CPR w/w; it may be related.)
For some reason, the effect does not happen if I use Int
instead of Integer
; then I get the pretty
Rec {
-- RHS size: {terms: 33, types: 42, coercions: 0, joins: 0/2}
Memo.$wgo [InlPrag=NOUSERINLINE[2], Occ=LoopBreaker]
:: GHC.Prim.Int# -> GHC.Prim.Int# -> (# Tree Int, Int, Tree Int #)
[GblId, Arity=2, Str=<L,U><L,U>, Unf=OtherCon []]
Memo.$wgo
= \ (ww_s1SE :: GHC.Prim.Int#) (ww1_s1SI :: GHC.Prim.Int#) ->
let {
s'_s1PK :: GHC.Prim.Int#
[LclId]
s'_s1PK = GHC.Prim.*# ww1_s1SI 2# } in
let {
l_s1PI :: GHC.Prim.Int#
[LclId]
l_s1PI = GHC.Prim.+# ww_s1SE ww1_s1SI } in
(# case Memo.$wgo l_s1PI s'_s1PK of
{ (# ww3_X1, ww4_X2, ww5_X3 #) ->
Memo.Tree @Int ww3_X1 ww4_X2 ww5_X3
},
GHC.Types.I# ww_s1SE,
case Memo.$wgo (GHC.Prim.+# l_s1PI ww1_s1SI) s'_s1PK of
{ (# ww3_X1, ww4_X2, ww5_X3 #) ->
Memo.Tree @Int ww3_X1 ww4_X2 ww5_X3
} #)
end Rec }
where we have no thunks and even unboxing. For Integer
, I cannot expect unboxing, but I do expect eager evaluation, given the bang patterns in the code!
(Reproduced with 8.10 and 9.0.)