Skip to content

Instantly share code, notes, and snippets.

@kisom
Last active August 29, 2015 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kisom/09e471e145bf1eca9124 to your computer and use it in GitHub Desktop.
Save kisom/09e471e145bf1eca9124 to your computer and use it in GitHub Desktop.
> import Control.Applicative
This post is a [literate
Haskell](https://gist.github.com/kisom/09e471e145bf1eca9124) file,
converted to org-mode with [Pandoc](http://pandoc.org).
Time to explore the `Applicative` type.
First, `fmap`:
fmap :: (Functor f) => (a -> b) -> f a -> b
A Functor applies some transformation on the values
of two functors. For example,
> maybeShow :: (Show a) => Maybe a -> Maybe String
> maybeShow x = show `fmap` x
```haskell
GHCi> maybeShow $ Just 1
Just "1"
GHCi> show `fmap` Just 1
Just "1"
GHCi> maybeShow Nothing
Nothing
```
There's a shortcut for `fmap` exported in
`Control.Applicative`: `<$>`.
> maybeShow' :: (Show a) => Maybe a -> Maybe String
> maybeShow' = (show <$>)
```haskell
GHCi> show <$> Just 1
Just "1"
```
Applicative is short for "Applicative Functor"; it
is a functor that can be applied. Basically, `a` is
a function; where we had `(a &rarr; b)` as the first
argument to `fmap`, we now have `f (a &rarr; b)`. The
`<*>` operator is used for sequential application:
```haskell
GHCi> :t (<*>)
(<*>) :: Applicative a => a (b -> c) -> a b -> a c
```
(Note: both `<$>` and `<*>` are left-associative with a
priority of 4.)
Let's try it this way:
> -- Login contains a username and password.
> data Login = Login String String deriving (Show)
Actually building login code is outside the scope of
this, so we'll simulate a scenario where a username can
be gotten and one where it can't; similarly for
passwords.
> getKyleName :: Maybe String
> getKyleName = Just "kyle"
>
> getBadName :: Maybe String
> getBadName = Nothing
The `Login` constructor basically acts as a function
that takes two string arguments and returns a login:
```
GHCi> :t Login
Login :: String -> String -> Login
```
Let's fmap Login with one of our login functions:
```
GHCi> :t Login <$> getBadName
Login <$> getBadName :: Maybe (String -> Login)
```
Notice the return type: it's an `f (a -> b)`. Let's
get some passwords:
> getMyPassword :: Maybe String
> getMyPassword = Just "password"
>
> getNoPassword :: Maybe String
> getNoPassword = Nothing
```haskell
GHCi> Login <$> getBadName <*> getNoPassword
Nothing
GHCi> Login <$> getKyleName <*> getMyPassword
Just (Login "kyle" "password")
GHCi> Login <$> getKyleName <*> getNoPassword
Nothing
GHCi> Login <$> getBadName <*> getMyPassword
Nothing
```
If any of the steps in the computation return `Nothing`,
the whole result is `Nothing`.
Notice `fmap` is used to parameterise the root value,
building a function from this. Successive links in
the application chain build on this. Imagine the
following:
> -- A flight crew has a commander, pilot, engineer,
> -- and medic.
> data FlightCrew = Crew String String String String
> deriving (Show)
> onePossibleCrew = Crew
> <$> Just "Mal"
> <*> Just "Wash"
> <*> Just "Kaylee"
> <*> Just "Book"
If we couldn't fill any of the roles, we wouldn't have
a crew.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment