functor-combinators-0.4.1.1: Tools for functor combinator-based program design
Copyright(c) Justin Le 2019
LicenseBSD3
Maintainerjustin@jle.im
Stabilityexperimental
Portabilitynon-portable
Safe HaskellSafe-Inferred
LanguageHaskell2010

Data.HFunctor.Route

Description

This module contains the useful combinators Pre and Post, which enhances a functor with a "route" to and from the outside world; even if the functor itself is existentially closed in a functor combinator, the route will provide line to the outside world for extraction or injection.

See Pre and Post for more information.

Since: 0.3.4.0

Synopsis

Routing Combinators

Contravariant

data Pre a f b Source #

A useful helper type to use with a covariant functor combinator that allows you to tag along contravariant access to all fs inside the combinator.

Maybe most usefully, it can be used with Ap. Remember that Ap f a is a collection of f xs, with each x existentially wrapped. Now, for a Ap (Pre a f) a, it will be a collection of f x and a -> xs: not only each individual part, but a way to "select" that individual part from the overal a.

So, you can imagine Ap (Pre a f) b as a collection of f x that consumes a and produces b.

When a and b are the same, Ap (Pre a f) a is like the free invariant sequencer. That is, in a sense, Ap (Pre a f) a contains both contravariant and covariant sequences side-by-side, consuming as and also producing as.

You can build up these values with injectPre, and then use whatever typeclasses your t normally supports to build it up, like Applicative (for Ap). You can then interpret it into both its contravariant and covariant contexts:

-- interpret the covariant part
runCovariant :: Applicative g => (f ~> g) -> Ap (Pre a f) a -> g a
runCovariant f = interpret (f . getPre)

-- interpret the contravariant part
runContravariant :: Divisible g => (f ~> g) -> Ap (Pre a f) a -> g a
runContravariant = preDivisible

The PreT type wraps up Ap (Pre a f) a into a type PreT Ap f a, with nice instances/helpers.

An example of a usage of this in the real world is the unjson library's record type constructor, to implement bidrectional serializers for product types.

Constructors

(a -> b) :>$<: (f b) infixl 4 

Instances

Instances details
HTraversable (Pre a :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

htraverse :: forall h f g (a0 :: k1). Applicative h => (forall (x :: k). f x -> h (g x)) -> Pre a f a0 -> h (Pre a g a0) Source #

HFunctor (Pre a :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

hmap :: forall (f :: k -> Type) (g :: k -> Type). (f ~> g) -> Pre a f ~> Pre a g Source #

a ~ Void => HBind (Pre a :: (Type -> Type) -> Type -> Type) Source #

This instance is over-contrained (a only needs to be uninhabited), but there is no commonly used "uninhabited" typeclass

Instance details

Defined in Data.HFunctor.Route

Methods

hbind :: forall (f :: k -> Type) (g :: k -> Type). (f ~> Pre a g) -> Pre a f ~> Pre a g Source #

hjoin :: forall (f :: k -> Type). Pre a (Pre a f) ~> Pre a f Source #

a ~ Void => Inject (Pre a :: (Type -> Type) -> Type -> Type) Source #

This instance is over-contrained (a only needs to be uninhabited), but there is no commonly used "uninhabited" typeclass

Instance details

Defined in Data.HFunctor.Route

Methods

inject :: forall (f :: k -> Type). f ~> Pre a f Source #

a ~ Void => Interpret (Pre a :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

retract :: Pre a f ~> f Source #

interpret :: forall (g :: k -> Type). (g ~> f) -> Pre a g ~> f Source #

Functor f => Functor (Pre a f) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

fmap :: (a0 -> b) -> Pre a f a0 -> Pre a f b #

(<$) :: a0 -> Pre a f b -> Pre a f a0 #

Contravariant f => Invariant (Pre a f) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

invmap :: (a0 -> b) -> (b -> a0) -> Pre a f a0 -> Pre a f b #

interpretPre :: Contravariant g => (f ~> g) -> Pre a f b -> g a Source #

Interpret a Pre into a contravariant context, applying the pre-routing function.

getPre :: Pre a f b -> f b Source #

Drop the pre-routing function and just give the original wrapped value.

retractPre :: Contravariant f => Pre a f b -> f a Source #

Contravariantly retract the f out of a Pre, applying the pre-routing function. Not usually that useful because Pre is meant to be used with covariant Functors.

injectPre :: Inject t => (a -> b) -> f b -> t (Pre a f) b Source #

Like inject, but allowing you to provide a pre-routing function.

mapPre :: (c -> a) -> Pre a f b -> Pre c f b Source #

Pre-compose on the pre-routing function.

preDivisible :: (forall m. Monoid m => Interpret t (AltConst m), Divisible g) => (f ~> g) -> t (Pre a f) b -> g a Source #

Run a "pre-routed" t into a contravariant Divisible context. To run it in ts normal covariant context, use interpret with getPre.

This will work for types where there are a possibly-empty collection of fs, like:

preDivisible :: Divisible g => (f ~> g) -> Ap    (Pre a f) b -> g a
preDivisible :: Divisible g => (f ~> g) -> ListF (Pre a f) b -> g a

preDivise :: (forall m. Semigroup m => Interpret t (AltConst m), Divise g) => (f ~> g) -> t (Pre a f) b -> g a Source #

Run a "pre-routed" t into a contravariant Divise context. To run it in ts normal covariant context, use interpret with getPre.

This will work for types where there are is a non-empty collection of fs, like:

preDivise :: Divise g => (f ~> g) -> Ap1       (Pre a f) b -> g a
preDivise :: Divise g => (f ~> g) -> NonEmptyF (Pre a f) b -> g a

preContravariant :: (forall m. Interpret t (AltConst m), Contravariant g) => (f ~> g) -> t (Pre a f) b -> g a Source #

Run a "pre-routed" t into a Contravariant. To run it in ts normal covariant context, use interpret with getPre.

This will work for types where there is exactly one f inside:

preContravariant :: Contravariant g => (f ~> g) -> Step     (Pre a f) b -> g a
preContravariant :: Contravariant g => (f ~> g) -> Coyoneda (Pre a f) b -> g a

Covariant

data Post a f b Source #

A useful helper type to use with a contravariant functor combinator that allows you to tag along covariant access to all fs inside the combinator.

Maybe most usefully, it can be used with Dec. Remember that Dec f a is a collection of f xs, with each x existentially wrapped. Now, for a Dec (Post a f) a, it will be a collection of f x and x -> as: not only each individual part, but a way to "re-embed" that individual part into overal a.

So, you can imagine Dec (Post a f) b as a collection of f x that consumes b and produces a.

When a and b are the same, Dec (Post a f) a is like the free invariant sequencer. That is, in a sense, Dec (Post a f) a contains both contravariant and covariant sequences side-by-side, consuming as and also producing as.

You can build up these values with injectPre, and then use whatever typeclasses your t normally supports to build it up, like Conclude (for Div). You can then interpret it into both its contravariant and covariant contexts:

-- interpret the covariant part
runCovariant :: Plus g => (f ~> g) -> Div (Post a f) a -> g a
runCovariant f = interpret (f . getPost)

-- interpret the contravariant part
runContravariant :: Conclude g => (f ~> g) -> Div (Post a f) a -> g a
runContravariant = preDivisible

The PostT type wraps up Dec (Post a f) a into a type PostT Dec f a, with nice instances/helpers.

An example of a usage of this in the real world is a possible implementation of the unjson library's sum type constructor, to implement bidrectional serializers for sum types.

Constructors

(b -> a) :<$>: (f b) infixl 4 

Instances

Instances details
HTraversable (Post a :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

htraverse :: forall h f g (a0 :: k1). Applicative h => (forall (x :: k). f x -> h (g x)) -> Post a f a0 -> h (Post a g a0) Source #

HFunctor (Post a :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

hmap :: forall (f :: k -> Type) (g :: k -> Type). (f ~> g) -> Post a f ~> Post a g Source #

Monoid a => HBind (Post a :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

hbind :: forall (f :: k -> Type) (g :: k -> Type). (f ~> Post a g) -> Post a f ~> Post a g Source #

hjoin :: forall (f :: k -> Type). Post a (Post a f) ~> Post a f Source #

Monoid a => Inject (Post a :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

inject :: forall (f :: k -> Type). f ~> Post a f Source #

Monoid a => Interpret (Post a :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

retract :: Post a f ~> f Source #

interpret :: forall (g :: k -> Type). (g ~> f) -> Post a g ~> f Source #

Contravariant f => Contravariant (Post a f) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

contramap :: (a' -> a0) -> Post a f a0 -> Post a f a' #

(>$) :: b -> Post a f b -> Post a f a0 #

Functor f => Invariant (Post a f) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

invmap :: (a0 -> b) -> (b -> a0) -> Post a f a0 -> Post a f b #

interpretPost :: Functor g => (f ~> g) -> Post a f b -> g a Source #

Interpret a Post into a covariant context, applying the post-routing function.

getPost :: Post a f b -> f b Source #

Drop the post-routing function and just give the original wrapped value.

retractPost :: Functor f => Post a f b -> f a Source #

Covariantly retract the f out of a Post, applying the post-routing function. Not usually that useful because Post is meant to be used with contravariant Functors.

injectPost :: Inject t => (b -> a) -> f b -> t (Post a f) b Source #

Like inject, but allowing you to provide a post-routing function.

mapPost :: (a -> c) -> Post a f b -> Post c f b Source #

Post-compose on the post-routing function.

postPlus :: (forall m. Monoid m => Interpret t (AltConst m), Plus g) => (f ~> g) -> t (Post a f) b -> g a Source #

Run a "post-routed" t into a covariant Plus context. To run it in ts normal contravariant context, use interpret.

This will work for types where there are a possibly-empty collection of fs, like:

postPlus :: Plus g => (f ~> g) -> Dec (Post a f) b -> g a
postPlus :: Plus g => (f ~> g) -> Div (Post a f) b -> g a

postAlt :: (forall m. Semigroup m => Interpret t (AltConst m), Alt g) => (f ~> g) -> t (Post a f) b -> g a Source #

Run a "post-routed" t into a covariant Alt context. To run it in ts normal contravariant context, use interpret.

This will work for types where there are is a non-empty collection of fs, like:

postAlt :: Alt g => (f ~> g) -> Dec1 (Post a f) b -> g a
postAlt :: Alt g => (f ~> g) -> Div1 (Post a f) b -> g a

postFunctor :: (forall m. Interpret t (AltConst m), Functor g) => (f ~> g) -> t (Post a f) b -> g a Source #

Run a "post-routed" t into a covariant Functor context. To run it in ts normal contravariant context, use interpret.

This will work for types where there is exactly one f inside:

postFunctor :: Functor g => (f ~> g) -> Step         (Post a f) b -> g a
postFunctor :: Functor g => (f ~> g) -> Coyoneda (Post a f) b -> g a

Wrapped Invariant

Contravariant

newtype PreT t f a Source #

Turn the covariant functor combinator t into an Invariant functor combinator; if t f a "produces" as, then PreT t f a will both consume and produce as.

You can run this normally as if it were a t f a by using interpret; however, you can also interpret into covariant contexts with preDivisibleT, preDiviseT, and preContravariantT.

A useful way to use this type is to use normal methods of the underlying t to assemble a final t, then using the PreT constructor to wrap it all up.

data MyType = MyType
     { mtInt    :: Int
     , mtBool   :: Bool
     , mtString :: String
     }

myThing :: PreT Ap MyFunctor MyType
myThing = PreT $ MyType
    $ injectPre mtInt    (mfInt    :: MyFunctor Int   )
    * injectPre mtBool   (mfBool   :: MyFunctor Bool  )
    * injectPre mtString (mfString :: MyFunctor STring)

See Pre for more information.

Constructors

PreT 

Fields

Instances

Instances details
HTraversable t => HTraversable (PreT t :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

htraverse :: forall h f g (a :: k1). Applicative h => (forall (x :: k). f x -> h (g x)) -> PreT t f a -> h (PreT t g a) Source #

HFunctor t => HFunctor (PreT t :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

hmap :: forall (f :: k -> Type) (g :: k -> Type). (f ~> g) -> PreT t f ~> PreT t g Source #

Inject t => Inject (PreT t :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

inject :: forall (f :: k -> Type). f ~> PreT t f Source #

Interpret t f => Interpret (PreT t :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

retract :: PreT t f ~> f Source #

interpret :: forall (g :: k -> Type). (g ~> f) -> PreT t g ~> f Source #

(HFunctor t, forall x. Functor (t (Pre x f))) => Invariant (PreT t f) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

invmap :: (a -> b) -> (b -> a) -> PreT t f a -> PreT t f b #

preDivisibleT :: (forall m. Monoid m => Interpret t (AltConst m), Divisible g) => (f ~> g) -> PreT t f ~> g Source #

Run a PreT t into a contravariant Divisible context. To run it in ts normal covariant context, use interpret.

This will work for types where there are a possibly-empty collection of fs, like:

preDivisibleT :: Divisible g => (f ~> g) -> PreT Ap    f ~> g
preDivisibleT :: Divisible g => (f ~> g) -> PreT ListF f ~> g

preDiviseT :: (forall m. Semigroup m => Interpret t (AltConst m), Divise g) => (f ~> g) -> PreT t f ~> g Source #

Run a PreT t into a contravariant Divise context. To run it in ts normal covariant context, use interpret.

This will work for types where there is a non-empty collection of fs, like:

preDiviseT :: Divise g => (f ~> g) -> PreT Ap1       f ~> g
preDiviseT :: Divise g => (f ~> g) -> PreT NonEmptyF f ~> g

preContravariantT :: (forall m. Interpret t (AltConst m), Contravariant g) => (f ~> g) -> PreT t f ~> g Source #

Run a PreT t into a Contravariant. To run it in ts normal covariant context, use interpret.

This will work for types where there is exactly one f inside:

preContravariantT :: Contravariant g => (f ~> g) -> PreT Step     f ~> g
preContravariantT :: Contravariant g => (f ~> g) -> PreT Coyoneda f ~> g

Covariant

newtype PostT t f a Source #

Turn the contravariant functor combinator t into an Invariant functor combinator; if t f a "consumes" as, then PostT t f a will both consume and produce as.

You can run this normally as if it were a t f a by using interpret; however, you can also interpret into covariant contexts with postPlusT, postAltT, and postFunctorT.

A useful way to use this type is to use normal methods of the underlying t to assemble a final t, then using the PreT constructor to wrap it all up.

myThing :: PostT Dec MyFunctor (Either Int Bool)
myThing = PostT $ decided $
    (injectPost Left  (mfInt  :: MyFunctor Int ))
    (injectPost Right (mfBool :: MyFunctor Bool))

See Post for more information.

Constructors

PostT 

Fields

Instances

Instances details
HTraversable t => HTraversable (PostT t :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

htraverse :: forall h f g (a :: k1). Applicative h => (forall (x :: k). f x -> h (g x)) -> PostT t f a -> h (PostT t g a) Source #

HFunctor t => HFunctor (PostT t :: (Type -> Type) -> Type -> Type) Source #

Since: 0.3.4.2

Instance details

Defined in Data.HFunctor.Route

Methods

hmap :: forall (f :: k -> Type) (g :: k -> Type). (f ~> g) -> PostT t f ~> PostT t g Source #

Inject t => Inject (PostT t :: (Type -> Type) -> Type -> Type) Source #

Since: 0.3.4.2

Instance details

Defined in Data.HFunctor.Route

Methods

inject :: forall (f :: k -> Type). f ~> PostT t f Source #

Interpret t f => Interpret (PostT t :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) Source #

Since: 0.3.4.2

Instance details

Defined in Data.HFunctor.Route

Methods

retract :: PostT t f ~> f Source #

interpret :: forall (g :: k -> Type). (g ~> f) -> PostT t g ~> f Source #

(HFunctor t, forall x. Contravariant (t (Post x f))) => Invariant (PostT t f) Source # 
Instance details

Defined in Data.HFunctor.Route

Methods

invmap :: (a -> b) -> (b -> a) -> PostT t f a -> PostT t f b #

postPlusT :: (forall m. Monoid m => Interpret t (AltConst m), Plus g) => (f ~> g) -> PostT t f ~> g Source #

Run a PostT t into a covariant Plus context. To run it in ts normal contravariant context, use interpret.

This will work for types where there are a possibly-empty collection of fs, like:

postPlusT :: Plus g => (f ~> g) -> PreT Dec f ~> g
postPlusT :: Plus g => (f ~> g) -> PreT Div f ~> g

postAltT :: (forall m. Semigroup m => Interpret t (AltConst m), Alt g) => (f ~> g) -> PostT t f ~> g Source #

Run a PostT t into a covariant Alt context. To run it in ts normal contravariant context, use interpret.

This will work for types where there is a non-empty collection of fs, like:

postAltT :: Alt g => (f ~> g) -> PreT Dec1 f ~> g
postAltT :: Alt g => (f ~> g) -> PreT Div1 f ~> g

postFunctorT :: (forall m. Interpret t (AltConst m), Functor g) => (f ~> g) -> PostT t f ~> g Source #

Run a PostT t into a covariant Functor context. To run it in ts normal contravariant context, use interpret.

This will work for types where there is exactly one f inside:

postFunctorT :: Functor g => (f ~> g) -> PreT Step f ~> g
postFunctorT :: Functor g => (f ~> g) -> PreT Coyoneda f ~> g