# Fun with Applicatives, Pt. III

Welcome back. This time we're going to wrap up our series on`Applicative`

s by taking a look into command line option parsing. Using the optparse-applicative package, we'll add flexibility to roller's CLI. Optparse-applicative leverages `Applicative`

to separate the handling of options parsing from the actual function that uses those options. It also provides some niceties like creating our program's help banner.Let's get started and see how it works.

## Option parsing

Here's what we want to accomplish. If we ask roller what it does (`$ roller -h`

), we should get back a usage message like this one.```
Usage: roller [-v|--verbose] [-n|--nrolls ARG] EXPR..
Roll some dice
Available options:
-h,--help Show this help text
-v,--verbose List out each roll
-n,--nrolls ARG Number of times to roll the expression
EXPR.. Dice expressions
```

So we can use our program to roll a dice expression multiple times:```
$ roller -n 3 2d10+2
10
12
9
```

Or, give us the actual values of each die roll generated:```
$ roller -v 2d10+2
[[4,5],[2]]
```

Or, even both:```
$ roller -v -n2 2d10+2
[[3,9],[2]]
[[5,3],[2]]
```

Pretty simple, eh? Now that we know what we're out to accomplish, we can start using the option parsing package and applicatives to make it happen, but first a quick detour.## Monoids

Optparse-applicative makes use of`Applicative`

s, but also `Monoid`

s, so it's worth taking a quick look into to those as well.A

`Monoid`

is a type equipped with a binary operation which is associative, along with a special element of the set which acts as the identity for the operation. Typically the operation can be thought of as some sort of combining or appending.The important bit is really that this operation is closed in the mathematical sense. That is if we combine two things of type X together, we get back an X. This new value can then be combined with another X, and so on, recursively. Such a closed operation affords us a lot of flexibility to build up interesting values by repeatedly combining smaller parts. If you're coming from an OO background, think of this as similar to the composite pattern.

We'll be treating option specifications monoidally: combining small specific options together until the result has the specific behavior we're after. So if we know we want to recognize an option like

`--foo`

, which has a short form `-f`

, we can do that quite easily by combining two primitive specs into a combined one^:```
l, s, myOpt :: OptionSpec -- ^1
l = long "foo"
s = short 'f'
myOpt = l <> s
```

^1 *Not the real type, optparse-applicative uses it's own internal type here, but logically we're just building up an option specification*

## Separation of concerns

Once we have a way to describe the individual flags we are concerned with, we want to have an abstract way to provide these values to the function that implements our application logic. Ideally, we can write our application in terms of the types that our application cares about, without having to deal with options at all.What does that mean for our application? We want to support having a switch that determines whether or not to give verbose output. We also need to have a flag which takes the number of values to produce. These values can be represented with a simple boolean and an integer. Further, we will need to accept the remaining arguments as the string representation of our dice expressions. Ultimately we want to write a function which takes these simple types and does something with them, i.e. our application will have the following type:

`type CLI a = Bool -> Int -> [String] -> a`

Once we have a function of type `CLI a`

, we can use the usual strategy of using `Applicative`

to lift our function into something that looks like this:`Opt Bool -> Opt Int -> Opt [String] -> Opt a`

where `Opt`

is just a fake type standing in for the libraries internal type representation for option parsers. We'll let it handle stringing together the different parsers, and just worry about implementing the `CLI`

.Putting this all together, we arrive at

`withOpts`

```
withOpts :: CLI a -> IO a
withOpts f = execParser . info (helper <*> handleOpts) $ infoMod
where
handleOpts =
f <$> switch ( long "verbose"
<> short 'v'
<> help "List out each roll")
<*> option ( long "nrolls"
<> value 1
<> short 'n'
<> help "Number of times to roll the expression")
<*> arguments1 str (metavar "EXPR.." <> help "Dice expressions")
infoMod = fullDesc <> progDesc "Roll some dice"
```

`withOpts`

takes the function which implements our logic as a parameter. It lifts that function `f`

using the `Applicative`

combinators into a function that operates over option parsers. Each argument between the `(<*>)`

operator describes how the option should be handled by gluing together basic parsers using monoidal combinations. Then we apply our passed in function. The `execParser . info (helper <*> handleOpts)`

bit just shuffles the types around to give the library what it expects, but the important part is in `handleOpts`

.It's worth noting this pattern of passing in the function

`f`

. It would be nice if we could just have a reference to the combined arguments, but `switch … <*> option …`

wouldn't be well-typed. If anyone knows another way around this limitation, please let us know!## Wrapping up

Now that we have our combinator that takes a`CLI`

description, we need to implement one that makes roller do what we want. Check out this definition of `rollEm`

.```
rollEm :: CLI (IO ())
rollEm verbose n args = maybe parseFail rollMany (parse input)
where
input = concat args
rollMany = replicateM_ n . rollOnce
rollOnce exp = fmap summary (roll exp) >>= putStrLn
summary = if verbose then show else show . sumRolls
sumRolls = sum . map sum
parseFail = putStrLn $ "Could not parse \"" ++ input ++ "\" as dice expression!"
```

Notice that we don't have to worry about options here at all. We just need to wrap this function inside of a call to `withOpts`

and then deal directly with primitive types for `verbose`

, `n`

and `args`

.Since

`withOpts`

takes `CLI (IO ())`

to `IO (IO ())`

, we need to use `join`

to untangle the actions and get back to `IO ()`

, which is a little messy, but it works great.```
main :: IO ()
main = join . withOpts $ rollEm
```

So, we've accomplished to write some nice concise code to implement a cool little tool. We we're able to use `Applicative`

s in at least three different ways; and hopefully, have picked up some usefull tricks along the way.Hope you've enjoyed this little tour. You can find a copy of the roller code on Github. If you have any questions, or just want to tell us what you thought of the series, let us know in the comments. Thanks for following along.

There are several ways of combining options without using CPS as in your `withOpts`:

ReplyDelete- Use a tuple:

(,,) <$> parser1 <*> parser2 <*> parser3

- Define the appropriate record type with meaningful field names and use its constructor instead of `(,,)`. This is what I usually do. It's a bit verbose, but it's by far the cleanest solution.

- Define your options in a parser of type `Parser (IO a)`. This is most easily achieved using the `Arrow` syntax. It can be a bit messy, but it probably the most direct way of using optparse-applicative. I wouldn't recommend it for big option sets, though.

Hi Paolo:

ReplyDeleteGood tips! Using the tuple is sort of similar to the example we did in part one to build up a list in the dice expression parser, but I definitely agree with you on point two being the cleanest. Creating a record type and mapping the value constructor seems to be the most idiomatic too from what I've seen.

Nice tour. Thanks!

ReplyDelete