csh
csh

Reputation: 65

I can't derive the type of fmap . const

While studying Haskell, I learned that <$ is defined as (<$) = fmap . const. I wanted to understand this by deriving its type and behavior from fmap, const, and (.).

However, no matter how much I tried, I couldn't derive the expected type Functor f => b -> f a -> f b, and when looking at fmap . const alone (without <$), I have no intuition about how it actually behaves.

Could someone help me understand this?

Upvotes: 1

Views: 64

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477462

(<$) = fmap . const

can be rewritten to:

(<$) x = fmap (const x)

which is then equivalent to:

(<$) x = fmap (\y -> x)

It is thus an functor-map that ignores the value to map, and repaces it by the value the <$ has taken.

For example for a list:

1 <$ [1, 4, 2, 5]

will return:

[1, 1, 1, 1]

and for Maybe, 42 <$ Just 73 is Just 42, whereas for 42 <$ Nothing, it is Nothing.

For IO, it thus generates an IO a that will run the IO actions, but repaces the "result" with a defined one, so:

42 <$ readLine

will ask for a line as value, but then return 42 alltogheter.

We can automatically derive the type by first looking at the types of the functions that are applied. (<$) = fmap . const is equivalent to:

(<$) = (.) fmap const

with (.) :: (b -> c) -> (a -> b) -> a -> c, fmap :: Functor f => (d -> e) -> f d -> f e, and const :: g -> h -> g, so we can derive the type with:

(.) ::               (    b ->        c     ) -> (a ->    b   ) -> a -> c
fmap :: Functor f =>  (d -> e) -> f d -> f e
const ::                                          g -> (h -> g)

so that gives us:

(.) ::               (    b ->        c     ) -> (a ->    b   ) -> a -> c
fmap :: Functor f =>  (d -> e) -> f d -> f e
const ::                                          g -> (h -> g)
-------------------------------------------------------------------------
b ~ d -> e
c ~ f d -> f e
a ~ g
b -> h -> g
d ~ h
g ~ e

Where x ~ y means that x and y are the same type.

The result of (.) fmap const, which thus has type a -> c can thus be "specialized" to:

(.) fmap const :: a -> c
(.) fmap const :: Functor f => g -> (f d -> f e)
(.) fmap const :: Functor f => e -> (f d -> f e)
(.) fmap const :: Functor f => e -> (f h -> f e)

so:

(.) fmap const :: Functor f => e -> (f h -> f e)

Upvotes: 3

Related Questions