What Functors Can Do For You

One of the major aspects of Functional Programming, its lack of side effects, can be one of its major stumbling blocks. Sure, taking a value as a parameter and using it without creating a variable is a simple matter for basic types, but what of more complicated structures? How does one go about unwrapping a structure, operating on its value, and re-wrapping the value in said structure without losing it’s data? The answer is by using a convoluted set of functions that take way too many arguments just to preserve the state. Or, you could use a functor.

So I Overload Operator()?

No, we aren’t talking about C++ Classes with an operator() implementation to make it look like a function. Functors in Haskell are a completely different animal. The easiest way to explain it is to look at an example. By way of example, let’s take a look at the Maybe monad:

data Maybe a = Nothing | Just a deriving (Eq, Ord)

As you can see, Maybe has two type constructors: Nothing or Just something. Maybe is used to represent a calculation that may have failed. If the calculation failed, Nothing is returned. If it succeeded, Just whatever is returned. This is all fine and good, but Just whatever isn’t the same thing as whatever; it is encapsulated in a maybe, and must be extracted if it is to be used.

How do you do that? Well, first you have to account for a Nothing value. Assuming you have a Just, you have to extract it, probably using a new function. Then you have to use the value. Afterwards, you probably want to put it back into the Maybe. Sounds like a lot of work, right? Let’s take a look at what Functor has to offer us:

class Functor f where fmap :: (a -> b) -> f a -> f b

So, a functor defines 1 function (actually it defines more, but they have default implementations and can be ignored in 99% of cases) fmap, which takes a function that takes 1 argument of type a and returns a result of type b, a Functor a, and returns a Functor b. Basically, this amounts to fmap takes a function, calls it on the passed in functor, and returns a functor with the resulting value.

Maybe This Is Helpful?

Let’s take a look at how this works with Maybe. Here is Maybe‘s Functor implementation:

instance Functor Maybe where fmap _ Nothing = Nothing fmap f (Just a) = Just (f a)

Basically, if fmap Nothing is called, Nothing is returned. Otherwise, the passed in function is called on the value contained in the Just, and a new Just is returned with the result. This is a much more elegant way to deal with structures, there is no checking required, fmap knows how to do the right thing.

A Bit Limiting

You may be thinking to yourself “Isn’t this a bit limiting? I can only fmap using a function that takes 1 argument!” You’d be right, if not for Haskell’s partial function application. Say we want to add 2 to the Int contained in a Just. If we didn’t have partial function application, we would have to do something ugly like this:

fmap (\a -> a + 2) (Just 2)

… or maybe something truly awful like this:

addTwo :: Int -> Int addTwo a = a + 2

Luckily for us, we live in a just and righteous world where we can just partially apply +:

fmap (+2) (Just 2)

While this is a trivial example, I feel it adequately illustrates the point; you can fmap a function that takes any amount of arguments if you partially apply it!

This leads me to an important point that most literature leaves out: you must apply arguments from left to right, the order of arguments is important! When you’re writing functions, think about how users might want to partially apply them. Take these two functions for example:

padString1 :: String -> Int -> String padString1 s 0 = s padString1 s n = padString1 (s ++ "-") (n - 1) padString2 :: Int -> String -> String padString2 0 s = s padString2 n s = padString2 (n - 1) (s ++ "-")

Both of these functions do the same thing: they take a String and an Int, and append n -‘s to the string. The difference is how they can be partially applied: padString1 has the String first, so it can only be partially applied with the String. Conversely, padString2 has the Int first, so it can only be partially applied with the Int. For this reason, when creating functions, take time to consider these issues. There’s probably a whole blog post to be written about not messing this up. Maybe when I feel qualified, I’ll write it! For now, just keep it in mind.

Sounds Familiar

You’ve heard this word before, right? Map… But where?

instance Functor [] where fmap = map

That’s right, Lists are Functors!. The fmap implementation for List just calls the map function that you’re probably familiar with. (if you need a refresher, it takes a function, a list, and returns a list with the function called on all members of the original list)

In fact, most structures in Haskell are Functors. All Monads in the standard library are Functors, so it’s a good bet that you can fmap them. Either way, judicious use of fmap and Functors can make your code much cleaner and more manageable.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: