Online Book Reader

Home Category

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

By Root 481 0
value or we just map each function over a functor value in succession. But with CMaybe it matters, because it keeps track of how many times it has been mapped over. Not cool! If we want CMaybe to obey the functor laws, we need to make it so that the Int field stays the same when we use fmap.

At first, the functor laws might seem a bit confusing and unnecessary. But if we know that a type obeys both laws, we can make certain assumptions about how it will act. If a type obeys the functor laws, we know that calling fmap on a value of that type will only map the function over it—nothing more. This leads to code that is more abstract and extensible, because we can use laws to reason about behaviors that any functor should have and make functions that operate reliably on any functor.

The next time you make a type an instance of Functor, take a minute to make sure that it obeys the functor laws. You can go over the implementation line by line and see if the laws hold or try to find a counterexample. Once you’ve dealt with enough functors, you will begin to recognize the properties and behaviors that they have in common, and begin to intuitively see if a type obeys the functor laws.

Using Applicative Functors

In this section, we’ll take a look at applicative functors, which are beefed-up functors.

So far, we have focused on mapping functions that take only one parameter over functors. But what happens when we map a function that takes two parameters over a functor? Let’s take a look at a couple of concrete examples of this.

If we have Just 3 and we call fmap (*) (Just 3), what do we get? From the instance implementation of Maybe for Functor, we know that if it’s a Just value, it will apply the function to the value inside the Just. Therefore, doing fmap (*) (Just 3) results in Just ((*) 3), which can also be written as Just (3 *) if we use sections. Interesting! We get a function wrapped in a Just!

Here are some more functions inside functor values:

ghci> :t fmap (++) (Just "hey")

fmap (++) (Just "hey") :: Maybe ([Char] -> [Char])

ghci> :t fmap compare (Just 'a')

fmap compare (Just 'a') :: Maybe (Char -> Ordering)

ghci> :t fmap compare "A LIST OF CHARS"

fmap compare "A LIST OF CHARS" :: [Char -> Ordering]

ghci> :t fmap (\x y z -> x + y / z) [3,4,5,6]

fmap (\x y z -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]

If we map compare, which has a type of (Ord a) => a -> a -> Ordering, over a list of characters, we get a list of functions of type Char -> Ordering, because the function compare gets partially applied with the characters in the list. It’s not a list of (Ord a) => a -> Ordering function, because the first a applied was a Char, and so the second a must decide to be of type Char.

We see how by mapping “multiparameter” functions over functor values, we get functor values that contain functions inside them. So now what can we do with them? For one, we can map functions that take these functions as parameters over them, because whatever is inside a functor value will be given to the function that we’re mapping over it as a parameter:

ghci> let a = fmap (*) [1,2,3,4]

ghci> :t a

a :: [Integer -> Integer]

ghci> fmap (\f -> f 9) a

[9,18,27,36]

But what if we have a functor value of Just (3 *) and a functor value of Just 5, and we want to take out the function from Just (3 *) and map it over Just 5? With normal functors, we’re out of luck, because they support only mapping normal functions over existing functors. Even when we mapped \f -> f 9 over a functor that contained functions, we were just mapping a normal function over it. But we can’t map a function that’s inside a functor value over another functor value with what fmap offers us. We could pattern match against the Just constructor to get the function out of it and then map it over Just 5, but we’re looking for a more general and abstract approach that works across functors.

Say Hello to Applicative


Meet the Applicative type class, in the Control.Applicative module. It defines two functions: pure and <*>. It doesn’t provide a default

Return Main Page Previous Page Next Page

®Online Book Reader