By: Comfortably Anonymous
Times Read: 879
Likes: 0 Dislikes: 0
Topic: News: Technical
IMO these things are best understood through the lens of Haskell's `Maybe` type.
Very simply, a `Maybe Int` for example can be either `Just 5` or `Nothing`. At its most basic form, it can be used with pattern matching. For example here's a haskell function that adds 2 to a Maybe if it's there:
add2 :: Maybe Int -> Maybe Int
add2 (Just n) = Just (n + 2)
add2 Nothing = Nothing
You can see we had to use pattern matching to "unwrap" the actual value of the maybe so we could add 2 to it. This is pretty inconvenient and pretty annoying especially if you're trying to do something more complex than simply adding 2. That's where `Functor` comes in.
`Functor` allows you to apply a function to the value inside the functor and get back the modified functor by using the `fmap` function. Here's what that definition looks like:
fmap :: Functor f => (a -> b) -> f a -> f b
This definition is a bit complex so I'll break it down. This is saying "fmap is a function which is defined for any `Functor` `f`. This function accepts a different function from `a` to `b`; and a functor containing an `a`; and returns a functor containing a `b`." `a` and `b` can be any type. What makes something a functor at the very base level is whether or not this function is defined. You can think about it like mapping over a list in any other language because that's exactly what `fmap` is defined as for lists.
So in our `Maybe` example, we can use it like so:
add2 :: Maybe Int -> Maybe Int
add2 x = fmap (+ 2) x
-- or
add2 x = (+ 2) <$> x -- <$> is just an infix version of fmap; same exact function.
Much more convenient, and as an added bonus it looks a bit more like regular function application (in haskell anyway).
We're not quite to monads yet because there's a step between `Functor` and `Monad` and that's called `Applicative`. Instead of starting with the (somewhat confusing) definition, let me pose a question: what if I have a function with more than one argument that I want to pass `Maybe`s into? Like what if I wanted to add two `Maybe` values together? That's the problem applicative solves.
With functors we can do:
(+ 2) <$> x
But with an applicative instance we can do:
(+) <$> maybeA <*> maybeB
the result of which will be a `Maybe` containing the result of adding the two values inside. If either of the `Maybe` values are `Nothing` it will come out as `Nothing`. And this pattern is extendable for more than just two arguments as well. For example:
functionWithManyNonMaybeArguments <$> maybeA <*> maybeB <*> maybeC <*> maybeD
So to quickly summarize: functors allow you to apply a function to the inside of one "wrapped" value (like `Maybe`). Applicatives allow you to apply a function of many arguments to many "wrapped" values. Now, here's where we get to monads. Here's a situation you might be in:
someFn :: a -> Maybe c
someMaybe :: Maybe a
How would you feed that `someMaybe` value into the `someFn` function? You might guess to use `fmap` from functor, but let's look at what would happen:
fmap someFn someMaybe
fmap :: Functor f => (a -> b) -> f a -> f b
-- so in this case
fmap :: Functor Maybe => (a -> Maybe c) -> Maybe a -> Maybe (Maybe c)
-- so
fmap someFn someMaybe :: Maybe (Maybe c)
Oof, double-wrapped `Maybe`; That's not right. This is the problem `Monad` solves with the `bind` function or `>>=` operator. The correct code for this would be:
bind :: Monad m => m a -> (a -> m b) -> m b -- again, >>= is just an infix version of bind; exact same thing.
someMaybe >>= someFn :: Maybe c
Also worth noting that for something to be a `Monad` it must also be `Applicative` and for something to be `Applicative` it must also be a `Functor`. So all monads are also applicatives and functors.
Sorry if you don't know any Haskell because this is pretty complex and very haskell-focused, but I kinda figured you wouldn't be asking these questions without a passing familiarity with some haskell. Feel free to ask any questions; I love talking about this stuff!