bwroga
bwroga

Reputation: 5459

Will call to fmap be removed when newtype is a Functor?

If I create a newtype:

newtype Bloo a = Bloo a 

And make it a Functor

instance Functor Bloo where
    fmap f (Bloo a) = Bloo $ f a

Will

fmap f (Bloo a)

be converted to

f a

in the compiled program?

Upvotes: 6

Views: 106

Answers (1)

Noughtmare
Noughtmare

Reputation: 10645

Almost certainly, yes, but it does require fmap to be inlined, which may not happen under certain circumstances. For example if it is deemed to large (not really relevant in this case) or if you annotate it with a {-# NOINLINE fmap #-} annotation. In those cases it will just be a function that applies f to its argument, like: fmap f x = f x.

You can try it out for yourself. Take this code for example:

-- Test.hs
newtype Test a = Test a
  deriving Show

instance Functor Test where
  fmap f (Test x) = Test (f x)

main = print $ fmap (+ 1) (Test 1)

And compile it with ghc Test.hs -ddump-simpl -dsuppress-all -fforce-recomp. That will output a bunch of stuff, but somewhere you will find the main function:


-- RHS size: {terms: 8, types: 9, coercions: 3, joins: 0/0}
main
  = $ (print ($fShowTest $fShowInteger))
      ((+ $fNumInteger 1 1) `cast` <Co:3>)

If you squint a bit, you might see that the fmap is gone and it is simply summing 1 and 1.

If you enable optimizations with -O1 then it will even evaluate that 1 + 1 at compile time:

-- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
main3 = 2

-- RHS size: {terms: 9, types: 11, coercions: 0, joins: 0/0}
main2
  = case $w$cshowsPrec4 11# main3 [] of { (# ww3_a2vc, ww4_a2vd #) ->
    : ww3_a2vc ww4_a2vd
    }

-- RHS size: {terms: 3, types: 1, coercions: 0, joins: 0/0}
main1 = ++ $fShowTest2 main2

-- RHS size: {terms: 4, types: 0, coercions: 0, joins: 0/0}
main = hPutStr' stdout main1 True

This output is a bit more obscure, but I hope you can recognize main3 = 2 which is the result of 1 + 1.

If you add a {-# NOINLINE fmap #-} pragma (and disable optimizations again) you will get:

-- RHS size: {terms: 13, types: 13, coercions: 3, joins: 0/1}
main
  = $ (print ($fShowTest $fShowInteger))
      ($cfmap1_r1CZ
         (let { ds_d1Cs = 1 } in
          \ ds1_d1Cr -> + $fNumInteger ds1_d1Cr ds_d1Cs)
         (1 `cast` <Co:3>))

Which is also complicated, but you might recognize $cfmap1_r1CZ which is a name-mangled version of the fmap function.

Upvotes: 11

Related Questions