You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/functors-applicative-functors-and-monoids.html
+6-6Lines changed: 6 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -118,7 +118,7 @@ <h1>Functors, Applicative Functors and Monoids</h1>
118
118
<p>How does the box analogy hold here? Well, if you stretch it, it holds. When we use <spanclass="fixed">fmap (+3)</span> over <spanclass="fixed">Just 3</span>, it’s easy to imagine the <spanclass="fixed">Maybe</span> as a box that has some contents on which we apply the function <spanclass="fixed">(+3)</span>. But what about when we’re doing <spanclass="fixed">fmap (*3) (+100)</span>? Well, you can think of the function <spanclass="fixed">(+100)</span> as a box that contains its eventual result. Sort of like how an I/O action can be thought of as a box that will go out into the real world and fetch some result. Using <spanclass="fixed">fmap (*3)</span> on <spanclass="fixed">(+100)</span> will create another function that acts like <spanclass="fixed">(+100)</span>, only before producing a result, <spanclass="fixed">(*3)</span> will be applied to that result. Now we can see how <spanclass="fixed">fmap</span> acts just like <spanclass="fixed">.</span> for functions.</p>
119
119
<p>The fact that <spanclass="fixed">fmap</span> is function composition when used on functions isn’t so terribly useful right now, but at least it’s very interesting. It also bends our minds a bit and let us see how things that act more like computations than boxes (<spanclass="fixed">IO</span> and <spanclass="fixed">(->) r</span>) can be functors. The function being mapped over a computation results in the same computation but the result of that computation is modified with the function.</p>
120
120
<imgsrc="assets/images/functors-applicative-functors-and-monoids/lifter.png" alt="lifting a function is easier than lifting a million pounds" class="right" width="443" height="450">
121
-
<p>Before we go on to the rules that <spanclass="fixed">fmap</span> should follow, let’s think about the type of <spanclass="fixed">fmap</span> once more. Its type is <spanclass="fixed">fmap :: (a -> b) -> f a -> f b</span>. We’re missing the class constraint <spanclass="fixed">(Functor f) =></span>, but we left it out here for brevity, because we’re talking about functors anyway so we know what the <spanclass="fixed">f</span> stands for. When we first learned about <ahref="higher-order-functions.html#curried-functions">curried functions</a>, we said that all Haskell functions actually take one parameter. A function <spanclass="fixed">a -> b -> c</span> actually takes just one parameter of type <spanclass="fixed">a</span> and then returns a function <spanclass="fixed">b -> c</span>, which takes one parameter and returns a <spanclass="fixed">c</span>. That’s how if we call a function with too few parameters (i.e. partially apply it), we get back a function that takes the number of parameters that we left out (if we’re thinking about functions as taking several parameters again). So <spanclass="fixed">a -> b -> c</span> can be written as <spanclass="fixed">a -> (b -> c)</span>, to make the currying more apparent.</p>
121
+
<p>Before we go on to the rules that <spanclass="fixed">fmap</span> should follow, let’s think about the type of <spanclass="fixed">fmap</span> once more. Its type is <spanclass="fixed">fmap :: (a -> b) -> f a -> f b</span>. We’re missing the class constraint <spanclass="fixed">(Functor f) =></span>, but we left it out here for brevity, because we’re talking about functors anyway so we know what the <spanclass="fixed">f</span> stands for. When we first learned about <ahref="higher-order-functions.html#curried-functions">curried functions</a>, we said that all Haskell functions actually take one parameter. A function <spanclass="fixed">a -> b -> c</span> actually takes just one parameter of type <spanclass="fixed">a</span> and then returns a function <spanclass="fixed">b -> c</span>, which takes one parameter and returns a <spanclass="fixed">c</span>. That’s how if we call a function with too few parameters (i.e. partially apply it), we get back a function that takes the number of parameters that we left out (if we’re thinking about functions as taking several parameters again). So <spanclass="fixed">a -> b -> c</span> can be written as <spanclass="fixed">a -> (b -> c)</span>, to make the currying more apparent.</p>
122
122
<p>In the same vein, if we write <spanclass="fixed">fmap :: (a -> b) -> (f a -> f b)</span>, we can think of <spanclass="fixed">fmap</span> not as a function that takes one function and a functor and returns a functor, but as a function that takes a function and returns a new function that’s just like the old one, only it takes a functor as a parameter and returns a functor as the result. It takes an <spanclass="fixed">a -> b</span> function and returns a function <spanclass="fixed">f a -> f b</span>. This is called <i>lifting</i> a function. Let’s play around with that idea by using GHCI’s <spanclass="fixed">:t</span> command:</p>
123
123
<prename="code" class="haskell:hs">
124
124
ghci> :t fmap (*2)
@@ -170,7 +170,7 @@ <h1>Functors, Applicative Functors and Monoids</h1>
170
170
<p>Seeing that mapping <spanclass="fixed">id</span> over a <spanclass="fixed">Nothing</span> value returns the same value is trivial. So from these two equations in the implementation for <spanclass="fixed">fmap</span>, we see that the law <spanclass="fixed">fmap id = id</span> holds.</p>
171
171
<imgsrc="assets/images/functors-applicative-functors-and-monoids/justice.png" alt="justice is blind, but so is my dog" class="left" width="345" height="428">
172
172
<p><em>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.</em> Formally written, that means that <spanclass="label law">fmap (f . g) = fmap f . fmap g</span>. Or to write it in another way, for any functor <i>F</i>, the following should hold: <spanclass="label law">fmap (f . g) F = fmap f (fmap g F)</span>.</p>
173
-
<p>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. We can know that when we use <spanclass="fixed">fmap</span> on it, there won’t be anything other than mapping going on behind the scenes and that it will act like a thing that can be mapped over, i.e.a functor. You figure out how the second law holds for some type by looking at the implementation of <spanclass="fixed">fmap</span> for that type and then using the method that we used to check if <spanclass="fixed">Maybe</span> obeys the first law.</p>
173
+
<p>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. We can know that when we use <spanclass="fixed">fmap</span> on it, there won’t be anything other than mapping going on behind the scenes and that it will act like a thing that can be mapped over, i.e.a functor. You figure out how the second law holds for some type by looking at the implementation of <spanclass="fixed">fmap</span> for that type and then using the method that we used to check if <spanclass="fixed">Maybe</span> obeys the first law.</p>
174
174
<p>If you want, we can check out how the second functor law holds for <spanclass="fixed">Maybe</span>. If we do <spanclass="fixed">fmap (f . g)</span> over <spanclass="fixed">Nothing</span>, we get <spanclass="fixed">Nothing</span>, because doing a <spanclass="fixed">fmap</span> with any function over <spanclass="fixed">Nothing</span> returns <spanclass="fixed">Nothing</span>. If we do <spanclass="fixed">fmap f (fmap g Nothing)</span>, we get <spanclass="fixed">Nothing</span>, for the same reason. OK, seeing how the second law holds for <spanclass="fixed">Maybe</span> if it’s a <spanclass="fixed">Nothing</span> value is pretty easy, almost trivial. </p><p>How about if it’s a <spanclass="fixed">Just <i>something</i></span> value? Well, if we do <spanclass="fixed">fmap (f . g) (Just x)</span>, we see from the implementation that it’s implemented as <spanclass="fixed">Just ((f . g) x)</span>, which is, of course, <spanclass="fixed">Just (f (g x))</span>. If we do <spanclass="fixed">fmap f (fmap g (Just x))</span>, we see from the implementation that <spanclass="fixed">fmap g (Just x)</span> is <spanclass="fixed">Just (g x)</span>. Ergo, <spanclass="fixed">fmap f (fmap g (Just x))</span> equals <spanclass="fixed">fmap f (Just (g x))</span> and from the implementation we see that this equals <spanclass="fixed">Just (f (g x))</span>.</p>
175
175
<p>If you’re a bit confused by this proof, don’t worry. Be sure that you understand how <ahref="higher-order-functions.html#composition">function composition</a> works. Many times, you can intuitively see how these laws hold because the types act like containers or functions. You can also just try them on a bunch of different values of a type and be able to say with some certainty that a type does indeed obey the laws.</p>
176
176
<p>Let’s take a look at a pathological example of a type constructor being an instance of the <spanclass="fixed">Functor</span> typeclass but not really being a functor, because it doesn’t satisfy the laws. Let’s say that we have a type:</p>
@@ -276,7 +276,7 @@ <h1>Functors, Applicative Functors and Monoids</h1>
276
276
ghci> Nothing <*> Just "woot"
277
277
Nothing
278
278
</pre>
279
-
<p>We see how doing <spanclass="fixed">pure (+3)</span> and <spanclass="fixed">Just (+3)</span> is the same in this case. Use <spanclass="fixed">pure</span> if you’re dealing with <spanclass="fixed">Maybe</span> values in an applicative context (i.e.using them with <spanclass="fixed"><*></span>), otherwise stick to <spanclass="fixed">Just</span>. The first four input lines demonstrate how the function is extracted and then mapped, but in this case, they could have been achieved by just mapping unwrapped functions over functors. The last line is interesting, because we try to extract a function from a <spanclass="fixed">Nothing</span> and then map it over something, which of course results in a <spanclass="fixed">Nothing</span>.</p>
279
+
<p>We see how doing <spanclass="fixed">pure (+3)</span> and <spanclass="fixed">Just (+3)</span> is the same in this case. Use <spanclass="fixed">pure</span> if you’re dealing with <spanclass="fixed">Maybe</span> values in an applicative context (i.e.using them with <spanclass="fixed"><*></span>), otherwise stick to <spanclass="fixed">Just</span>. The first four input lines demonstrate how the function is extracted and then mapped, but in this case, they could have been achieved by just mapping unwrapped functions over functors. The last line is interesting, because we try to extract a function from a <spanclass="fixed">Nothing</span> and then map it over something, which of course results in a <spanclass="fixed">Nothing</span>.</p>
280
280
<p>With normal functors, you can just map a function over a functor and then you can’t get the result out in any general way, even if the result is a partially applied function. Applicative functors, on the other hand, allow you to operate on several functors with a single function. Check out this piece of code:</p>
281
281
<prename="code" class="haskell:hs">
282
282
ghci> pure (+) <*> Just 3 <*> Just 5
0 commit comments