data-functor-logistic: Updatable analogue of Distributive functors

[ bsd3, data-structures, library ] [ Propose Tags ]


[Skip to Readme]


[Index] [Quick Jump]


Maintainer's Corner

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS] 0.0
Change log
Dependencies base (>=4.9 && <4.17), distributive [details]
License BSD-3-Clause
Copyright Copyright (c) 2021 Fumiaki Kinoshita
Author Fumiaki Kinoshita
Category Data Structures
Bug tracker
Source repo head: git clone
Uploaded by FumiakiKinoshita at 2021-11-16T08:22:25Z
Distributions NixOS:0.0
Downloads 52 total (2 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2021-11-16 [all 1 reports]

Readme for data-functor-logistic-0.0

[back to package description]

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:

class Functor g => Distributive g where
  distribute :: Functor f => f (g a) -> g (f a)

One easy-to-understand instance is Complex.

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:

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:

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.

newtype Op a b = Op { getOp :: b -> a }

setters :: Logistic t => t ((a -> a) -> t a -> t a)
setters = getOp <$> deliver (Op id)
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.