packages feed

implicit-params-0.2: README.md

# Implicit parameters

There are certain types of applications which are configurable where it makes
sense to model this configurability as a global or semi-global set of
configuration values that some or all parts of the program can "implicitly"
access. These configuration values are called "implicit parameters".

## `ImplicitParams` in Haskell

Haskell already has support for implicit parameters via the `ImplicitParams`
[extension][ImplicitParams]. However, `ImplicitParams` has several flaws and
is barely used at all in modern Haskell code. Many Haskellers consider its
(ab)use of `let`/`where` bindings to pass implicit parameters to be ugly.
Also, it's questionable how "implicit" the implicit parameters of
`ImplicitParams` actually are, as they show up in the context of the type
signature of any function which uses them. There's also no way you can call a
function taking an implicit parameter without passing it that parameter if it
isn't already in context: i.e., there is no way to specify a "default" value
for an implicit parameter if none is passed.

## Motivating example

`implicit-params` solves some of these problems and introduces new problems of
its own. However, there is one particular use case which motivated me to
develop `implicit-params` that isn't supported by the existing
`ImplicitParams` extension. Imagine you have the following code:

    app :: Config -> IO ()
    app config = doStuffWith config
    
    defaultConfig :: Config
    defaultConfig = ...

Which is used by a program as follows:

    main = app defaultConfig

There are two problems with this code. One is that `app` has to manually plumb
the `Config` value it was given around everywhere. One solution here would be
for `app` to use a [Reader][Reader] monad internally, but that can complicate
the code in some ways and it seems like overkill. If it used the
`ImplicitParams` extension, the above code would look like this:

    app :: (?config :: Config) => IO ()
    app = doStuffWith ?config
    
    defaultConfig :: Config
    defaultConfig = ...

    main = let ?config = defaultConfig in app

You can see why `ImplicitParams` isn't very highly regarded: it made the type
signature of `app` longer, as well as the code that uses it. However, at least
the internals of `app` will be somewhat nicer now as the `Config` value won't
have to be manually plumbed around everywhere.

## `data-default`

The [`data-default`][data-default] package provides a type class `Default`
which represents the class of types which have a "default" value. It has a
single operation `def` which returns the default value for a given type (the
type is given by type inference). Using `Default` the above code could be made
a little nicer:

    app :: (?config :: Config) => IO ()
    app = doStuffWith ?config
    
    instance Default Config where def = ...

    main = let ?config = def in app

However, the above code is still so *ugly* considering what we're trying to
do: all we want to do is run `app` with the defaults. This should be as simple
as typing `app`, and only if we're overriding the defaults should the code
need to be any longer than this. This is exactly what `implicit-params` does.
If an implicit parameter is not explicitly given to a function which requires
it, its value is given by `def` for the `Default` instance for the type of the
parameter. And if the type does not have a `Default` instance, then it is a
type error to call that function without explicitly setting the implicit
parameter (but it will work fine if you do set it). This is how the above code
looks using `implicit-params`:

    app :: Implicit_ Config => IO ()
    app = doStuffWith param_
    
    instance Default Config where def = defaultConfig

    main = app

Perfect! What if we want to pass a non-default config to `app`? That's easy
too:

    main = setParam_ (def {option = 1}) app

`setParam_` even has an infix synonym `$~` which makes the above code even
nicer:

    main = app $~ def {option = 1}

(Bonus points for not abusing `let`/`where` bindings.)

### Named implicit parameters

The above code uses unnamed implicit parameters, which will suffice for most
code. Sometimes you might want to pass more than one implicit parameter of the
same type to a single function, and for this you need some way of selecting
the particular implicit parameter on which to operate. `implicit-params` uses
type level [symbols][Symbols] for this, which require the `DataKinds`
[extension][DataKinds].

`Implicit_`, which is used in the examples above, denotes an unnamed implicit
parameter. `Implicit "foo"` can be used to denote a named implicit parameter
named `"foo"`. Named implicit parameters are slightly more awkward to use
because they require passing [`Proxy`][Proxy] parameters to the `param` and
`setParam` functions to specify the names of the implicit parameters on which
they are to operate. See the Haddock documentation of the `Data.Implicit`
module for more details.

## Acknowledgements

This wouldn't be possible without techniques that I learnt from
[Edward Kmett][edwardk] and [Philip JF][philipjf]. In particular, this package
uses ideas from Edward's packages [tagged][tagged], [constraints][constraints]
and [reflection][reflection] and Philip's blog posts [Haskell Supports
First-Class Instances][firstclass] and [Using Compiler Bugs for Fun and
Profit: Introducing Cartesian Closed Constraints][profit].

[ImplicitParams]: http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-extensions.html#implicit-parameters
[Reader]: http://hackage.haskell.org/packages/archive/mtl/latest/doc/html/Control-Monad-Reader-Class.html
[data-default]: http://hackage.haskell.org/package/data-default
[Symbols]: http://www.haskell.org/ghc/docs/latest/html/libraries/base/GHC-TypeLits.html#t:Symbol
[DataKinds]: http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/kind-polymorphism-and-promotion.html
[Proxy]: http://hackage.haskell.org/packages/archive/tagged/latest/doc/html/Data-Proxy.html
[edwardk]: http://comonad.com/reader/
[philipjf]: http://joyoftypes.blogspot.com/
[tagged]: http://hackage.haskell.org/package/tagged
[constraints]: http://hackage.haskell.org/package/constraints
[reflection]: http://hackage.haskell.org/package/reflection
[firstclass]: http://joyoftypes.blogspot.com/2012/02/haskell-supports-first-class-instances.html
[profit]: http://joyoftypes.blogspot.com/2013/01/using-compiler-bugs-for-fun-and-profit.html