## Per-use strict dictionaries

## Motivation

A dictionary with more than one element is implemented as a struct, and each of its elements is actually an accessor. The `-fdicts-strict`

option changes this, so that (for instance) a function which depends on a dictionary deconstructs it like a case statement would, but it applies to an entire compilation unit. It would be nice if it could be applied on a case-by-case basis without having to cut related functions into different files depending on whether they use strict dicts or not.

An example of where this would be helpful is in Edward Kmett's folds library. A common pattern is something like

```
import Data.Fold
-- This is turned into something like foldMap with
-- Profunctor functions like dimap.
toFoldM :: Monoid m => M m m
toFoldM = M id id mappend mempty
```

We know we have a `Monoid`

dictionary at that point, yet what is stored in the `M`

value is something along the lines of `dict_mappend monoidDict`

and `dict_mempty monoidDict`

, which is unnecessary indirection. `-fdicts-strict`

would make this an actual deconstruction, but it would apply to the whole file, whether you need it or not.

## Proposal

There should be a pragma for forcing, or trying to force at least, a dictionary to be accessed strictly. If the pragma is called `STRICTDICT`

, then the previous example would become

```
import Data.Fold
toFoldM :: {-# STRICTDICT #-} Monoid m => M m m
toFoldM = M id id mappend mempty
```

And the core would become something like

```
toFoldM :: Monoid m -> M m m
toFoldM monoidDict = case monoidDict of
Monoid _semigroup memptyHere mappendHere _mconcat -> M id id mappendHere memptyHere
```

There would also be a `LAZYDICT`

pragma for turning off strict dictionaries at a declaration site, and perhaps for parent dictionaries (as described below).

`STRICTDICT`

could also be used in GADTs, where it would be like the `!`

modifier on a dictionary:

```
data IsOrd a where
IsOrd :: {-# STRICTDICT #-} Ord a => IsOrd a
```

It could also be used in instance declarations:

```
instance {-# STRICTDICT #-} Num a => Monoid (Sum a) where
mempty = Sum (fromInteger 0)
mappend = (coerce :: (a -> a -> a) -> Sum a -> Sum a -> Sum a) (+)
```

This would produce core which would resemble:

```
monoidDictSum :: Num a -> Monoid (Sum a)
monoidDictSum numDict = case numDict of
Num { fromInteger, (+) } = let
semigroupHere = semigroupDictSum numDict
memptyHere = Sum (fromInteger 0)
mappendHere = coerce (+)
mconcatHere = mconcatDefault monoidHere
monoidHere = Monoid semigroupHere memptyHere mappendHere mconcatHere
in monoidHere
```

It could also be used in class declarations, and would even unbox the parent dictionary if it was small enough:

```
class {-# STRICTDICT #-} Eq a => Ord a where
...
```

This would cause copies of `(==)`

and `(/=)`

to be included in the `Ord`

dictionary.

However, it shouldn't be allowed inside a rank N type:

```
-- Not allowed
newtype SBazaar a b t = SBazaar { runSBazaar :: forall f. {-# STRICTDICT #-} Applicative f => (a -> f b) -> f t }
```

Some of you may have noted that, at some point soon, `mappend`

will be removed from the `Monoid`

class, and the above example would use `<>`

from `Semigroup`

instead. So the question becomes, is the parent of a strictly accessed dictionary also accessed strictly? The principle of least surprise seems to say that it should, but it might possibly lead to an infinite regress.

If you want to load a dictionary strictly, but one of its parents lazily, you could do something like this:

```
traversingBar :: ({-# STRICTDICT #-} Applicative f, {-# LAZYDICT #-} Functor f) => (Foo -> f Foo) -> Bar -> f Bar
traversingBar = ...
```

However, if discussion leads to the parents not being used strictly, then you would put `STRICTDICT`

on both of those constraints in the declaration.

You could even hypothetically import fixed dictionaries strictly or lazily, like `{-# STRICTDICT #-} Monoid (Sum Int)`

This would trigger a warning without the pragma, but it probabaly shouldn't with it,, since by using it you presumably know what you're asking.

`STRICTDICT`

and `LAZYDICT`

shouldn't give a warning on dictionaries with one element, because it's possible for them to get different amounts during refactoring. However, it wouldn't force the dictionary itself, just like unwrapping a `newtype`

doesn't.

`STRICTDICT`

and `LAZYDICT`

should give a warning on the builtin zero-size constraints `~`

, `~~`

, and `Coercible`

, because that evidence is (a) not actually passed in as an argument, and (b) can't be acted on lazily anyway. However, even a declaration like `{-# LAZYDICT #-} (~) a b`

shouldn't alter the semantics of the program.