File: README.md

package info (click to toggle)
haskell-data-functor-logistic 0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 72 kB
  • sloc: haskell: 64; makefile: 2
file content (71 lines) | stat: -rw-r--r-- 2,121 bytes parent folder | download
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.