# capability: effects, extensionally

[![Build status](https://badge.buildkite.com/a44574059e13fc443caa437226af5ae5e67b6a5ae012534712.svg?branch=master)](https://buildkite.com/tweag-1/capability)

A capability is a type class that says explicitly which effects
a function is allowed to use. The [`mtl`][mtl] works like this too.
But unlike the `mtl`, this library decouples effects from their
implementation. What this means in practice:

- You can implement large sets of capabilities using the
  efficient [`ReaderT` pattern][readert], rather than a slow monad
  transformer stack.
- Capabilities compose well: e.g. it's easy to have multiple reader
  effects.
- You can use a writer effect without implementing it as a writer
  monad (which is known to [leak space][writer-space-leak]).
- You can reason about effects. For instance, if a monad provides a
 reader effect at type `IORef A`, it also provides a state effect at type `A`

For more on these, you may want to read the announcement [blog
 post][blog].

This library is an alternative to the [`mtl`][mtl]. It defines a set
of standard, reusable capability type classes, such as the `HasReader`
and `HasState` type classes, which provide the standard reader and
state effects, respectively.

Where `mtl` instances only need to be defined once and for all,
capability-style programming has traditionally suffered from verbose
boilerplate: rote instance definitions for every new implementation of
the capability. Fortunately GHC 8.6 introduced
the [`DerivingVia`][deriving-via] language extension. We use it to
remove the boilerplate, turning capability-style programming into an
appealing alternative to `mtl`-style programming. The
[`generic-lens`][generic-lens] library is used to access fields of
structure in the style of the [`ReaderT` pattern][readert].

An additional benefit of separating capabilities from their
implementation is that they avoid a pitfall of the `mtl`. In the
`mtl`, two different `MonadState` are disambiguated by their types,
which means that it is difficult to have two `MonadState Int` in the
same monad stack. Capability type classes are parameterized by a name
(also known as a *tag*). This makes it possible to combine multiple
versions of the same capability. For example,

```haskell
twoStates :: (HasState "a" Int m, HasState "b" Int m) => m ()
```

Here, the tags `"a"` and `"b"` refer to different state spaces.

In summary, compared to the `mtl`:

- capabilities represent what effects a function can use, rather than
  how the monad is constructed;
- capabilities are named, rather than disambiguated by type;
- capabilites are discharged with deriving-via combinators
  and [`generic-lens`][generic-lens], rather than with instance
  resolution.

An example usage looks like this:

``` haskell
testParity :: (HasReader "foo" Int m, HasState "bar" Bool m) => m ()
testParity = do
  num <- ask @"foo"
  put @"bar" (even num)

data Ctx = Ctx { foo :: Int, bar :: IORef Bool }
  deriving Generic

newtype M a = M { runM :: Ctx -> IO a }
  deriving (Functor, Applicative, Monad) via ReaderT Ctx IO
  -- Use DerivingVia to derive a HasReader instance.
  deriving (HasReader "foo" Int) via
    -- Pick the field foo from the Ctx record in the ReaderT environment.
    Field "foo" "ctx" (MonadReader (ReaderT Ctx IO))
  -- Use DerivingVia to derive a HasState instance.
  deriving (HasState "bar" Bool) via
    -- Convert a reader of IORef to a state capability.
    ReaderIORef (Field "bar" "ctx" (MonadReader (ReaderT Ctx IO)))

example :: IO ()
example = do
    rEven <- newIORef False
    runM testParity (Ctx 2 rEven)
    readIORef rEven >>= print
    runM testParity (Ctx 3 rEven)
    readIORef rEven >>= print
```

For more complex examples, see the [Examples section](#examples) and
the [`examples` subtree](./examples).

API documentation can be found on
[Hackage](http://hackage.haskell.org/package/capability).

[circleci]: https://circleci.com/gh/tweag/capabilities-via/tree/master
[mtl]: http://hackage.haskell.org/package/mtl
[blog]: https://www.tweag.io/posts/2018-10-04-capability.html
[deriving-via]: https://downloads.haskell.org/~ghc/8.6.1/docs/html/users_guide/glasgow_exts.html#deriving-via
[generic-lens]: https://hackage.haskell.org/package/generic-lens
[readert]: https://www.fpcomplete.com/blog/2017/06/readert-design-pattern
[writer-space-leak]: https://blog.infinitenegativeutility.com/2016/7/writer-monads-and-space-leaks

## Examples

An example is provided in [`WordCount`](examples/WordCount.hs).
Execute the following commands to try it out:

```
$ nix-shell --pure --run "cabal configure --enable-tests"
$ nix-shell --pure --run "cabal repl examples"

ghci> :set -XOverloadedStrings
ghci> wordAndLetterCount "ab ba"
Letters
'a': 2
'b': 2
Words
"ab": 1
"ba": 1
```

To execute all examples and see if they produce the expected results run

```
$ nix-shell --pure --run "cabal test examples --show-details=streaming --test-option=--color"
```

## Build instructions

### Nix Shell

Some of this package's dependencies require patches to build with GHC 8.6.
These patches are defined in
[`nix/haskell/default.nix`](nix/haskell/default.nix).
A development environment with all patched dependencies in scope is defined in
[`shell.nix`](shell.nix).

### Build

The build instructions assume that you have [Nix][nix] installed.
Execute the following command to build the library.

```
$ nix-shell --pure --run "cabal configure"
$ nix-shell --pure --run "cabal build"
```

[nix]: https://nixos.org/nix/