data-functor-logistic: Updatable analogue of Distributive functors

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


[Skip to Readme]


[Index] [Quick Jump]


Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS] 0.0
Change log
Dependencies base (>=4.9 && <4.20), distributive [details]
License BSD-3-Clause
Copyright Copyright (c) 2021 Fumiaki Kinoshita
Author Fumiaki Kinoshita
Revised Revision 2 made by Bodigrim at 2023-10-30T20:54:27Z
Category Data Structures
Bug tracker
Source repo head: git clone
Uploaded by FumiakiKinoshita at 2021-11-16T08:22:25Z
Distributions Arch:0.0, LTSHaskell:0.0, NixOS:0.0, Stackage:0.0
Reverse Dependencies 1 direct, 23 indirect [details]
Downloads 424 total (13 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.