I have a question regarding sharing in haskell (a functional programing language).
Given
f n | n > 0 = n
I am interested how the following expression would be evaluated step by step. Is it:
f (2*3) + f 6 = f 6 + f 6 = 6 + 6 = 12
Or is it:
f (2*3) + f 6 = f 6 + f 6 = 6 + f 6 = 6 + 6 = 12
It would be pretty shocking if GHC "noticed" the duplicated argument and eliminated it for you. This kind of peephole analysis is not technically impossible, but it would often end up costing you much more than it gains. Imagine you had
g x = f x + f 6
If the compiler wanted to avoid excess calls to f
, it could check each time whether x == 6
, and if so share the result. But most of the time x /= 6
, and so this extra check costs you time. If you think that optimization will fire often enough to be useful, you can of course make it yourself.
I also looked at the output of -ddump-simpl
, to observe the Core that GHCI produces. I believe this output is after all optimization steps have run, so it would be a definitive answer that, at least in GHCI, this optimization is not performed; however, I'm not sure of this.
Note that there are two unconditional calls to f
here.
$ ghci -ddump-simpl
...
Prelude> f (2 * 3) + f 6
==================== Simplified expression ====================
let {
it_a2Cx :: forall a. (GHC.Num.Num a, GHC.Classes.Ord a) => a
[LclId,
Arity=2,
Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True,
WorkFree=True, Expandable=True, Guidance=IF_ARGS [150 0] 550 0}]
it_a2Cx
= \ (@ a_a2Yl)
($dNum_a2YF :: GHC.Num.Num a_a2Yl)
($dOrd_a2YG :: GHC.Classes.Ord a_a2Yl) ->
GHC.Num.+
@ a_a2Yl
$dNum_a2YF
(Ghci1.f
@ a_a2Yl
$dOrd_a2YG
$dNum_a2YF
(GHC.Num.*
@ a_a2Yl
$dNum_a2YF
(GHC.Num.fromInteger @ a_a2Yl $dNum_a2YF 2)
(GHC.Num.fromInteger @ a_a2Yl $dNum_a2YF 3)))
(Ghci1.f
@ a_a2Yl
$dOrd_a2YG
$dNum_a2YF
(GHC.Num.fromInteger @ a_a2Yl $dNum_a2YF 6)) } in
GHC.Base.thenIO
@ ()
@ [()]
(System.IO.print
@ GHC.Integer.Type.Integer
GHC.Show.$fShowInteger
(it_a2Cx
@ GHC.Integer.Type.Integer
GHC.Num.$fNumInteger
GHC.Integer.Type.$fOrdInteger))
(GHC.Base.returnIO
@ [()]
(GHC.Types.:
@ ()
(it_a2Cx
`cast` (UnsafeCo representational (forall a.
(GHC.Num.Num a, GHC.Classes.Ord a) =>
a) ()
:: (forall a. (GHC.Num.Num a, GHC.Classes.Ord a) => a :: *)
~R# (() :: *)))
(GHC.Types.[] @ ())))
12