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?
Functor in Haskell is a kind of functional representation of different Types which can be mapped over. It is a high level concept of implementing polymorphism. According to Haskell developers, all the Types such as List, Map, Tree, etc. are the instance of the Haskell Functor.
Functor is also important in its role as a superclass of Applicative and of Traversable . When working with these more powerful abstractions, it's often very useful to reach for the fmap method. Show activity on this post. For example, it's possible to derive the function lift in a way that works for any functor.
Another simple example of a functor is the Maybe type. This object can contain a value of a particular type as Just , or it is Nothing (like a null value).
String is not a functor, because it has the wrong kind.
The mapping operation by itself does not modify the values in the functor, only the function. The structure of the functor remains unchanged and only the values are modified. fmap returns an identical functor as the original, with its values swapped to the result of calling a given function with the original value as an argument.
fmap (f . g) == fmap f . fmap g If two sequential mapping operations are performed one after the other using two functions, the result should be the same as a single mapping operation with one function that is equivalent to applying the first function to the result of the second.
Functors are required to obey certain laws in regards to their mapping. Ensuring instances of Functor obey these laws means the behaviour of fmap remains predictable. When performing the mapping operation, if the values in the functor are mapped to themselves, the result will be an unmodified functor. fmap (f . g) == fmap f . fmap g
The C++ implementation of fmap also ignores the function argument and essentially re-casts the Const argument without changing its value: template<class C, class A, class B> Const<C, B> fmap (std::function<B (A)> f, Const<C, A> c) { return Const<C, B> {c._v}; } Despite its weirdness, the Const functor plays an important role in many constructions.
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.
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