# capability: effects, extensionally
[](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, HasSource "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, HasSource "bar" Bool, HasSink "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
A development environment with all 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/