Lukas Hohl
Lukas Hohl

Reputation: 69

Haskell sharing : Is it determined what is shared when the code is compiled?

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

Upvotes: 0

Views: 267

Answers (1)

amalloy
amalloy

Reputation: 92117

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

Upvotes: 2

Related Questions