(not to be confused with the function const
)
newtype Const a b = Const { getConst :: a }
instance Functor (Const a) where
fmap _ (Const a) = Const a
Const functor holds no elements. Sorry that's not precisely true. Const
by itself is not a functor. Const a
is the functor. Const
is simply a type constructor. When you try to map (fmap f
) this functor, the mapping function is simply dropped, and the returned result is the same thing as before. It becomes a "constant" functor, a functor that doesn't change.
import Control.Applicative (Const(Const))
let object1 = Const "hello" -- Const String b
let object2 = fmap (&& False) object1 -- Functor f => f Bool
let object3 = fmap (\_ -> 1.2 :: Double) object2 -- Functor f => f Double
getConst object3 -- "hello"
We can see that Functor f => f b
is the same as Const String b
. The type inference at the point of mapping the functor forgets about Const String
and just considers it to be a functor f b
. The b
in the end doesn't matter. It is just inferred to whatever the last mapping function returns, but in the end it doesn't do anything. In the end we can still get the "hello" string out of the object. Thus it's a functor that doesn't hold anything, and is useful we need to hide some value that can be carried in the computational context while the context is being mapped.
There is also Data.Functor.Constant
, it operates in the same way.
(not to be confused with the function id
)
newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity where
fmap f (Identity x) = Identity (f x)
Identity functor holds only 1 element. When you try mapping this functor, it just applies to the function to the value and returns. It's a trivial functor that has no significance in its computational context.
import Data.Functor.Identity
let object1 = Identity "meh" -- Functor f => f [Char]
let object2 = fmap (const "yawn") object1
let Identity x = object2
x -- "yawn"
runIdentity object2 -- "yawn"
It can be used to wrap a variable in a trivial functor so that it can be mapped by a lifted operator. It can also serve as the base monad (monads are functors) to which a series of monad transformed may be applied to construct a composite monad.
data (->) a b
instance Functor ((->) r) where
fmap = (.)
Functions are also functors. The function data type declaration has no value constructors. This is because function declaration is special, and is done with a equal sign or lambda syntax. Any function that accepts one parameter is a functor. Mapping the functor is equivalent to composition. The function's body acts like the computational context.
let object1 = (+) 1
let object2 = fmap object1 (*2) -- Num a => a -> a
let object3 = fmap object2 (*3)
let object4 = fmap object3 (/2) -- Fractional a => a -> a
object4 5 -- 16.0
let object1 = (+) 1
let object2 = fmap (*2) object1
let object3 = fmap (*3) object2
let object4 = fmap (/2) object3 -- Fractional a => a -> a
object4 5 -- 18.0
One major difference is that because we can fmap
a function to a function, we can choose the order of composition. The 1st iteration goes backwards (/2, *3, *2, +1), the 2nd iteration goes forwards (+1, *2, *3, /2). This is because functions are contravariant functors a.k.a. "cofunctors". Most functors are covariant functors like the functors defined above.
Furthermore the extraction of the value inside the functor's computational context is done by applying the resulting function to one more value. In essence the value that we are fmapping
is the computational itself. Everytime we map the functor, we are sequentially changing the computation. In order to know what the computation does at the end, we have to put a value in. Applying the value is the equivalent expression to using runIdentity
or getConst
.