Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [100]

By Root 508 0
:t fmap (replicate 3)

fmap (replicate 3) :: (Functor f) => f a -> f [a]

The expression fmap (*2) is a function that takes a functor f over numbers and returns a functor over numbers. That functor can be a list, a Maybe, an Either String, or anything else. The expression fmap (replicate 3) will take a functor over any type and return a functor over a list of elements of that type. This is even more apparent if we partially apply, say, fmap (++"!") and then bind it to a name in GHCi.

You can think of fmap in two ways:

As a function that takes a function and a functor value and then maps that function over the functor value

As a function that takes a function and lifts that function so it operates on functor values

Both views are correct.

The type fmap (replicate 3) :: (Functor f) => f a -> f [a] means that the function will work on any functor. What it will do depends on the functor. If we use fmap (replicate 3) on a list, the list’s implementation for fmap will be chosen, which is just map. If we use it on Maybe a, it will apply replicate 3 to the value inside the Just. If it’s Nothing, it stays Nothing. Here are some examples:

ghci> fmap (replicate 3) [1,2,3,4]

[[1,1,1],[2,2,2],[3,3,3],[4,4,4]]

ghci> fmap (replicate 3) (Just 4)

Just [4,4,4]

ghci> fmap (replicate 3) (Right "blah")

Right ["blah","blah","blah"]

ghci> fmap (replicate 3) Nothing

Nothing

ghci> fmap (replicate 3) (Left "foo")

Left "foo"

Functor Laws

All functors are expected to exhibit certain kinds of properties and behaviors. They should reliably behave as things that can be mapped over. Calling fmap on a functor should just map a function over the functor—nothing more. This behavior is described in the functor laws. All instances of Functor should abide by these two laws. They aren’t enforced by Haskell automatically, so you need to test them yourself when you make a functor. All the Functor instances in the standard library obey these laws.

Law 1


The first functor law states that if we map the id function over a functor value, the functor value that we get back should be the same as the original functor value. Written a bit more formally, it means that fmap id = id. So essentially, this says that if we do fmap id over a functor value, it should be the same as just applying id to the value. Remember that id is the identity function, which just returns its parameter unmodified. It can also be written as \x -> x. If we view the functor value as something that can be mapped over, the fmap id = id law seems kind of trivial or obvious.

Let’s see if this law holds for a few values of functors.

ghci> fmap id (Just 3)

Just 3

ghci> id (Just 3)

Just 3

ghci> fmap id [1..5]

[1,2,3,4,5]

ghci> id [1..5]

[1,2,3,4,5]

ghci> fmap id []

[]

ghci> fmap id Nothing

Nothing

Looking at the implementation of fmap for Maybe, for example, we can figure out why the first functor law holds:

instance Functor Maybe where

fmap f (Just x) = Just (f x)

fmap f Nothing = Nothing

We imagine that id plays the role of the f parameter in the implementation. We see that if we fmap id over Just x, the result will be Just (id x), and because id just returns its parameter, we can deduce that Just (id x) equals Just x. So now we know that if we map id over a Maybe value with a Just value constructor, we get that same value back.

Seeing that mapping id over a Nothing value returns the same value is trivial. So from these two equations in the implementation for fmap, we find that the law fmap id = id holds.

Law 2


The second law says that composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one. Formally written, that means fmap (f . g) = fmap f . fmap g. Or to write it in another way, for any functor value x, the following should hold: fmap (f . g) x = fmap f (fmap g x).

If we can show that some type obeys both functor laws, we can rely on it having the same fundamental behaviors as other functors when it comes to mapping.

Return Main Page Previous Page Next Page

®Online Book Reader