1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
Logistic is to setters as Distributive is to getters
----
Distributive functors are containers where getters can be enumerated as their own types.
This is the definition of the `Distributive` class:
```haskell
class Functor g => Distributive g where
distribute :: Functor f => f (g a) -> g (f a)
```
One easy-to-understand instance is `Complex`.
```haskell
data Complex a = !a :+ !a
realPart :: Complex a -> a
realPart (x :+ _) = x
imagPart :: Complex a -> a
imagPart (_ :+ y) = y
instance Distributive Complex where
distribute wc = fmap realPart wc :+ fmap imagPart wc
```
Given any functor-wrapped value, `distribute` fmaps the getters of `Complex` to it.
`distribute id` instantiates it as the function (`(->) r`) functor. In this case, `distribute id` is equal to `realPart :+ imagPart`.
However, we cannot modify the elements this way because `distribute` passes getters but not setters.
Here we introduce a new `Logistic` class to provide settability to containers:
```haskell
class Functor t => Logistic t where
deliver :: Contravariant f => f (t a -> t a) -> t (f (a -> a))
```
While the type of `deliver` is slightly more intimidating, it's actually very close to the `distribute`;
the `Functor` constraint is `Contravariant` instead and the contents are endomorphisms.
Here's the instance for `Complex`. `deliver f` contramaps a setter function to `f` for each field:
```haskell
instance Logistic Complex where
deliver f
= contramap (\g (a :+ b) -> g a :+ b) f
:+ contramap (\g (a :+ b) -> a :+ g b) f
```
Instantiating the `Op` contravariant functor, it is trivial to obtain a collection of setters.
```haskell
newtype Op a b = Op { getOp :: b -> a }
setters :: Logistic t => t ((a -> a) -> t a -> t a)
setters = getOp <$> deliver (Op id)
```
```haskell
ghci> let setR :+ setI = setters
ghci> setR (+1) (0 :+ 1)
1 :+ 1
ghci> setI (+1) (0 :+ 1)
0 :+ 2
```
`deliver` has a generic default implementation which works for any single-constructor products.
This class can be useful to complement `Distributive`. Generalisation to higher-kinded data would also be interesting.
|