Copyright | (c) Justin Le 2019 |
---|---|
License | BSD3 |
Maintainer | justin@jle.im |
Stability | experimental |
Portability | non-portable |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
This module provides tools for working with binary functor combinators.
Data.Functor.HFunctor deals with single functor combinators (transforming a single functor). This module provides tools for working with combinators that combine and mix two functors "together".
The binary analog of HFunctor
is HBifunctor
: we can map
a structure-transforming function over both of the transformed functors.
Tensor
gives some extra properties of your binary functor combinator:
associativity and identity (see docs for Tensor
for more details).
The binary analog of Interpret
is MonoidIn
. If your combinator t
and target functor f
is an instance of
, it means you
can "interpret" out of your tensored values, and also "generate" values
of MonoidIn
t ff
.
biretract
:: (f:+:
f) a -> f apureT
::V1
a -> f a biretract ::Plus
f => (f:*:
f) a -> f a pureT :: Plus f =>Proxy
a -> f a biretract ::Applicative
f =>Day
f f a -> f a pureT :: Applicative f =>Identity
a -> f a biretract ::Monad
f =>Comp
f f a -> f a pureT :: Monad f =>Identity
a -> f a
Synopsis
- class (Associative t, Inject (ListBy t)) => Tensor t i | t -> i where
- type ListBy t :: (Type -> Type) -> Type -> Type
- intro1 :: f ~> t f i
- intro2 :: g ~> t i g
- elim1 :: FunctorBy t f => t f i ~> f
- elim2 :: FunctorBy t g => t i g ~> g
- appendLB :: t (ListBy t f) (ListBy t f) ~> ListBy t f
- splitNE :: NonEmptyBy t f ~> t f (ListBy t f)
- splittingLB :: ListBy t f <~> (i :+: t f (ListBy t f))
- toListBy :: t f f ~> ListBy t f
- fromNE :: NonEmptyBy t f ~> ListBy t f
- rightIdentity :: (Tensor t i, FunctorBy t f) => f <~> t f i
- leftIdentity :: (Tensor t i, FunctorBy t g) => g <~> t i g
- sumLeftIdentity :: f <~> (V1 :+: f)
- sumRightIdentity :: f <~> (f :+: V1)
- prodLeftIdentity :: f <~> (Proxy :*: f)
- prodRightIdentity :: g <~> (g :*: Proxy)
- class (Tensor t i, SemigroupIn t f) => MonoidIn t i f where
- nilLB :: forall t i f. Tensor t i => i ~> ListBy t f
- consLB :: Tensor t i => t f (ListBy t f) ~> ListBy t f
- unconsLB :: Tensor t i => ListBy t f ~> (i :+: t f (ListBy t f))
- retractLB :: forall t i f. MonoidIn t i f => ListBy t f ~> f
- interpretLB :: forall t i g f. MonoidIn t i f => (g ~> f) -> ListBy t g ~> f
- inL :: forall t i f g. MonoidIn t i g => f ~> t f g
- inR :: forall t i f g. MonoidIn t i f => g ~> t f g
- outL :: (Tensor t Proxy, FunctorBy t f) => t f g ~> f
- outR :: (Tensor t Proxy, FunctorBy t g) => t f g ~> g
- prodOutL :: (f :*: g) ~> f
- prodOutR :: (f :*: g) ~> g
- newtype WrapF f a = WrapF {
- unwrapF :: f a
- newtype WrapLB t f a = WrapLB {}
- class Tensor t i => Matchable t i where
- splittingNE :: (Matchable t i, FunctorBy t f) => NonEmptyBy t f <~> t f (ListBy t f)
- matchingLB :: forall t i f. (Matchable t i, FunctorBy t f) => ListBy t f <~> (i :+: NonEmptyBy t f)
Tensor
class (Associative t, Inject (ListBy t)) => Tensor t i | t -> i where Source #
An Associative
HBifunctor
can be a Tensor
if there is some
identity i
where t i f
and t f i
are equivalent to just f
.
That is, "enhancing" f
with t i
does nothing.
The methods in this class provide us useful ways of navigating
a
with respect to this property.Tensor
t
The Tensor
is essentially the HBifunctor
equivalent of Inject
,
with intro1
and intro2
taking the place of inject
.
Formally, we can say that t
enriches a the category of
endofunctors with monoid strcture: it turns our endofunctor category
into a "monoidal category".
Different instances of t
each enrich the endofunctor category in
different ways, giving a different monoidal category.
type ListBy t :: (Type -> Type) -> Type -> Type Source #
The "monoidal functor combinator" induced by t
.
A value of type ListBy t f a
is equivalent to one of:
I a
-- zero fsf a
-- one ft f f a
-- two fst f (t f f) a
-- three fst f (t f (t f f)) a
t f (t f (t f (t f f))) a
- .. etc
For example, for :*:
, we have ListF
. This is because:
Proxy
~ListF
[] ~nilLB
@(:*:
) x ~ ListF [x] ~inject
x x :*: y ~ ListF [x,y] ~toListBy
(x :*: y) x :*: y :*: z ~ ListF [x,y,z] -- etc.
You can create an "empty" one with nilLB
, a "singleton" one with
inject
, or else one from a single t f f
with toListBy
.
See NonEmptyBy
for a "non-empty"
version of this type.
Because t f (I t)
is equivalent to f
, we can always "insert"
f
into t f (I t)
.
This is analogous to inject
from Inject
, but for HBifunctor
s.
Because t (I t) g
is equivalent to f
, we can always "insert"
g
into t (I t) g
.
This is analogous to inject
from Inject
, but for HBifunctor
s.
elim1 :: FunctorBy t f => t f i ~> f Source #
Witnesses the property that i
is the identity of t
: t
f i
always leaves f
unchanged, so we can always just drop the
i
.
elim2 :: FunctorBy t g => t i g ~> g Source #
Witnesses the property that i
is the identity of t
: t i g
always leaves g
unchanged, so we can always just drop the i t
.
appendLB :: t (ListBy t f) (ListBy t f) ~> ListBy t f Source #
If a
represents multiple applications of ListBy
t ft f
to
itself, then we can also "append" two
s applied to
themselves into one giant ListBy
t f
containing all of the ListBy
t ft f
s.
Note that this essentially gives an instance for
, for any functor SemigroupIn
t (ListBy t f)f
; this is witnessed by
WrapLB
.
splitNE :: NonEmptyBy t f ~> t f (ListBy t f) Source #
Lets you convert an
into a single application of NonEmptyBy
t ff
to
.ListBy
t f
Analogous to a function NonEmpty
a -> (a,
[a])
Note that this is not reversible in general unless we have
.Matchable
t
splittingLB :: ListBy t f <~> (i :+: t f (ListBy t f)) Source #
An
is either empty, or a single application of ListBy
t ft
to f
and ListBy t f
(the "head" and "tail"). This witnesses that
isomorphism.
toListBy :: t f f ~> ListBy t f Source #
Embed a direct application of f
to itself into a
.ListBy
t f
fromNE :: NonEmptyBy t f ~> ListBy t f Source #
is "one or more NonEmptyBy
t ff
s", and 'ListBy t f
is "zero or more
f
s". This function lets us convert from one to the other.
This is analogous to a function
.NonEmpty
a ->
[a]
Note that because t
is not inferrable from the input or output
type, you should call this using -XTypeApplications:
fromNE
@(:*:
) ::NonEmptyF
f a ->ListF
f a fromNE @Comp
::Free1
f a ->Free
f a
Instances
rightIdentity :: (Tensor t i, FunctorBy t f) => f <~> t f i Source #
f
is isomorphic to t f i
: that is, i
is the identity of t
, and
leaves f
unchanged.
leftIdentity :: (Tensor t i, FunctorBy t g) => g <~> t i g Source #
g
is isomorphic to t i g
: that is, i
is the identity of t
, and
leaves g
unchanged.
sumLeftIdentity :: f <~> (V1 :+: f) Source #
leftIdentity
(intro1
and elim1
) for :+:
actually does not
require Functor
. This is the more general version.
sumRightIdentity :: f <~> (f :+: V1) Source #
rightIdentity
(intro2
and elim2
) for :+:
actually does not
require Functor
. This is the more general version.
prodLeftIdentity :: f <~> (Proxy :*: f) Source #
leftIdentity
(intro1
and elim1
) for :*:
actually does not
require Functor
. This is the more general version.
prodRightIdentity :: g <~> (g :*: Proxy) Source #
rightIdentity
(intro2
and elim2
) for :*:
actually does not
require Functor
. This is the more general version.
MonoidIn
class (Tensor t i, SemigroupIn t f) => MonoidIn t i f where Source #
This class effectively gives us a way to generate a value of f a
based on an i a
, for
. Having this ability makes a lot
of interesting functions possible when used with Tensor
t ibiretract
from
SemigroupIn
that weren't possible without it: it gives us a "base
case" for recursion in a lot of cases.
Essentially, we get an i ~> f
, pureT
, where we can introduce an f
a
as long as we have an i a
.
Formally, if we have
, we are enriching the category of
endofunctors with monoid structure, turning it into a monoidal category.
Different choices of Tensor
t it
give different monoidal categories.
A functor f
is known as a "monoid in the (monoidal) category
of endofunctors on t
" if we can biretract
:
t f f ~> f
and also pureT
:
i ~> f
This gives us a few interesting results in category theory, which you can stil reading about if you don't care:
- All functors are monoids in the monoidal category
on
:+:
- The class of functors that are monoids in the monoidal
category on
:*:
is exactly the functors that are instances ofPlus
. - The class of functors that are monoids in the monoidal
category on
Day
is exactly the functors that are instances ofApplicative
. - The class of functors that are monoids in the monoidal
category on
Comp
is exactly the functors that are instances ofMonad
.
This is the meaning behind the common adage, "monads are just monoids
in the category of endofunctors". It means that if you enrich the
category of endofunctors to be monoidal with Comp
, then the class
of functors that are monoids in that monoidal category are exactly
what monads are. However, the adage is a little misleading: there
are many other ways to enrich the category of endofunctors to be
monoidal, and Comp
is just one of them. Similarly, the class of
functors that are monoids in the category of endofunctors enriched by
Day
are Applicative
.
Note that instances of this class are intended to be written with t
and i
to be fixed type constructors, and f
to be allowed to vary
freely:
instance Monad f => MonoidIn Comp Identity f
Any other sort of instance and it's easy to run into problems with type
inference. If you want to write an instance that's "polymorphic" on
tensor choice, use the WrapHBF
and WrapF
newtype wrappers over type
variables, where the third argument also uses a type constructor:
instance MonoidIn (WrapHBF t) (WrapF i) (MyFunctor t i)
This will prevent problems with overloaded instances.
Nothing
If we have an i
, we can generate an f
based on how it
interacts with t
.
Specialized (and simplified), this type is:
pureT
@Day
::Applicative
f =>Identity
a -> f a --pure
pureT @Comp
::Monad
f => Identity a -> f a --return
pureT @(:*:
) ::Plus
f =>Proxy
a -> f a --zero
Note that because t
appears nowhere in the input or output types,
you must always use this with explicit type application syntax (like
pureT @Day
)
Along with biretract
, this function makes f
a monoid in the
category of endofunctors with respect to tensor t
.
Instances
Conclude f => MonoidIn Night Not f Source # | Instances of |
Inplus f => MonoidIn Night Not f Source # | Since: 0.4.0.0 |
(Apply f, Applicative f) => MonoidIn Day Identity f Source # | Instances of Note that because of typeclass constraints, this requires |
Inplicative f => MonoidIn Day Identity f Source # | |
(Divise f, Divisible f) => MonoidIn Day (Proxy :: Type -> Type) f Source # | Instances of Note that because of typeclass constraints, this requires Since: 0.3.0.0 |
Alt f => MonoidIn These1 (V1 :: Type -> Type) f Source # | |
(Bind f, Monad f) => MonoidIn (Comp :: (Type -> Type) -> (Type -> Type) -> Type -> Type) Identity f Source # | Instances of This instance is the "proof" that "monads are the monoids in the
category of endofunctors (enriched with Note that because of typeclass constraints, this requires |
Plus f => MonoidIn (Product :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (Proxy :: Type -> Type) f Source # | Instances of |
MonoidIn (Sum :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (V1 :: Type -> Type) f Source # | All functors are monoids in the monoidal category on |
Plus f => MonoidIn ((:*:) :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (Proxy :: Type -> Type) f Source # | Instances of |
MonoidIn ((:+:) :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (V1 :: Type -> Type) f Source # | All functors are monoids in the monoidal category on |
(Tensor t i, FunctorBy t f, FunctorBy t (WrapLB t f)) => MonoidIn (WrapHBF t) (WrapF i) (WrapLB t f) Source # | |
(Tensor t i, FunctorBy t (Chain t i f)) => MonoidIn (WrapHBF t) (WrapF i) (Chain t i f) Source # |
|
nilLB :: forall t i f. Tensor t i => i ~> ListBy t f Source #
Create the "empty ListBy
".
If
represents multiple applications of ListBy
t ft f
with
itself, then nilLB
gives us "zero applications of f
".
Note that t
cannot be inferred from the input or output type of
nilLB
, so this function must always be called with -XTypeApplications:
nilLB
@Day
::Identity
~>
Ap
f nilLB @Comp
:: Identity ~>Free
f nilLB @(:*:
) ::Proxy
~>ListF
f
Note that this essentially gives an instance for
, for any functor MonoidIn
t i (ListBy
t f)f
; this is witnessed by WrapLB
.
consLB :: Tensor t i => t f (ListBy t f) ~> ListBy t f Source #
Lets us "cons" an application of f
to the front of an
.ListBy
t f
Utility
inL :: forall t i f g. MonoidIn t i g => f ~> t f g Source #
Convenient wrapper over intro1
that lets us introduce an arbitrary
functor g
to the right of an f
.
You can think of this as an HBifunctor
analogue of inject
.
inR :: forall t i f g. MonoidIn t i f => g ~> t f g Source #
Convenient wrapper over intro2
that lets us introduce an arbitrary
functor f
to the right of a g
.
You can think of this as an HBifunctor
analogue of inject
.
A newtype wrapper meant to be used to define polymorphic MonoidIn
instances. See documentation for MonoidIn
for more information.
Please do not ever define an instance of MonoidIn
"naked" on the
third parameter:
instance MonidIn (WrapHBF t) (WrapF i) f
As that would globally ruin everything using WrapHBF
.
Instances
Foldable f => Foldable (WrapF f) Source # | |
Defined in Data.HBifunctor.Tensor fold :: Monoid m => WrapF f m -> m # foldMap :: Monoid m => (a -> m) -> WrapF f a -> m # foldMap' :: Monoid m => (a -> m) -> WrapF f a -> m # foldr :: (a -> b -> b) -> b -> WrapF f a -> b # foldr' :: (a -> b -> b) -> b -> WrapF f a -> b # foldl :: (b -> a -> b) -> b -> WrapF f a -> b # foldl' :: (b -> a -> b) -> b -> WrapF f a -> b # foldr1 :: (a -> a -> a) -> WrapF f a -> a # foldl1 :: (a -> a -> a) -> WrapF f a -> a # elem :: Eq a => a -> WrapF f a -> Bool # maximum :: Ord a => WrapF f a -> a # minimum :: Ord a => WrapF f a -> a # | |
Eq1 f => Eq1 (WrapF f) Source # | |
Ord1 f => Ord1 (WrapF f) Source # | |
Defined in Data.HBifunctor.Tensor | |
Show1 f => Show1 (WrapF f) Source # | |
Traversable f => Traversable (WrapF f) Source # | |
Functor f => Functor (WrapF f) Source # | |
(Typeable a, Typeable f, Typeable k, Data (f a)) => Data (WrapF f a) Source # | |
Defined in Data.HBifunctor.Tensor gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> WrapF f a -> c (WrapF f a) # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (WrapF f a) # toConstr :: WrapF f a -> Constr # dataTypeOf :: WrapF f a -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c (WrapF f a)) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c (WrapF f a)) # gmapT :: (forall b. Data b => b -> b) -> WrapF f a -> WrapF f a # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> WrapF f a -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> WrapF f a -> r # gmapQ :: (forall d. Data d => d -> u) -> WrapF f a -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> WrapF f a -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> WrapF f a -> m (WrapF f a) # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> WrapF f a -> m (WrapF f a) # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> WrapF f a -> m (WrapF f a) # | |
Generic (WrapF f a) Source # | |
Read (f a) => Read (WrapF f a) Source # | |
Show (f a) => Show (WrapF f a) Source # | |
Eq (f a) => Eq (WrapF f a) Source # | |
Ord (f a) => Ord (WrapF f a) Source # | |
Defined in Data.HBifunctor.Tensor | |
Tensor t i => Tensor (WrapHBF t) (WrapF i) Source # | |
Defined in Data.HBifunctor.Tensor intro1 :: forall (f :: Type -> Type). f ~> WrapHBF t f (WrapF i) Source # intro2 :: forall (g :: Type -> Type). g ~> WrapHBF t (WrapF i) g Source # elim1 :: forall (f :: Type -> Type). FunctorBy (WrapHBF t) f => WrapHBF t f (WrapF i) ~> f Source # elim2 :: forall (g :: Type -> Type). FunctorBy (WrapHBF t) g => WrapHBF t (WrapF i) g ~> g Source # appendLB :: forall (f :: Type -> Type). WrapHBF t (ListBy (WrapHBF t) f) (ListBy (WrapHBF t) f) ~> ListBy (WrapHBF t) f Source # splitNE :: forall (f :: Type -> Type). NonEmptyBy (WrapHBF t) f ~> WrapHBF t f (ListBy (WrapHBF t) f) Source # splittingLB :: forall (f :: Type -> Type). ListBy (WrapHBF t) f <~> (WrapF i :+: WrapHBF t f (ListBy (WrapHBF t) f)) Source # toListBy :: forall (f :: Type -> Type). WrapHBF t f f ~> ListBy (WrapHBF t) f Source # fromNE :: forall (f :: Type -> Type). NonEmptyBy (WrapHBF t) f ~> ListBy (WrapHBF t) f Source # | |
(Tensor t i, FunctorBy t f, FunctorBy t (WrapLB t f)) => MonoidIn (WrapHBF t) (WrapF i) (WrapLB t f) Source # | |
(Tensor t i, FunctorBy t (Chain t i f)) => MonoidIn (WrapHBF t) (WrapF i) (Chain t i f) Source # |
|
type Rep (WrapF f a) Source # | |
Defined in Data.HBifunctor.Tensor |
Any
is a ListBy
t f
and a SemigroupIn
t
, if we
have MonoidIn
t i
. This newtype wrapper witnesses that fact. We
require a newtype wrapper to avoid overlapping instances.Tensor
t i
Instances
Contravariant (ListBy t f) => Contravariant (WrapLB t f) Source # | Since: 0.3.0.0 |
Functor (ListBy t f) => Functor (WrapLB t f) Source # | |
Invariant (ListBy t f) => Invariant (WrapLB t f) Source # | Since: 0.3.0.0 |
Defined in Data.HBifunctor.Tensor | |
(Tensor t i, FunctorBy t f, FunctorBy t (WrapLB t f)) => SemigroupIn (WrapHBF t) (WrapLB t f) Source # | |
(Tensor t i, FunctorBy t f, FunctorBy t (WrapLB t f)) => MonoidIn (WrapHBF t) (WrapF i) (WrapLB t f) Source # | |
Matchable
class Tensor t i => Matchable t i where Source #
For some t
, we have the ability to "statically analyze" the
and pattern match and manipulate the structure without ever
interpreting or retracting. These are ListBy
tMatchable
.
unsplitNE :: FunctorBy t f => t f (ListBy t f) ~> NonEmptyBy t f Source #
The inverse of splitNE
. A consing of f
to
is
non-empty, so it can be represented as an ListBy
t f
.NonEmptyBy
t f
This is analogous to a function
.uncurry
(:|
)
:: (a, [a]) -> NonEmpty
a
matchLB :: FunctorBy t f => ListBy t f ~> (i :+: NonEmptyBy t f) Source #
"Pattern match" on an
: it is either empty, or it is
non-empty (and so can be an ListBy
t f
).NonEmptyBy
t f
This is analgous to a function
.nonEmpty
:: [a]
-> Maybe (NonEmpty
a)
Note that because t
cannot be inferred from the input or output
type, you should use this with -XTypeApplications:
matchLB
@Day
::Ap
f a -> (Identity
:+:Ap1
f) a
Note that you can recursively "unroll" a ListBy
completely into
a Chain
by using
unrollLB
.
Instances
Matchable Night Not Source # | Since: 0.3.0.0 |
Matchable Night Not Source # | |
Matchable Day Identity Source # | |
Matchable Day Identity Source # | |
Matchable Day (Proxy :: Type -> Type) Source # | Instances of Since: 0.3.0.0 |
Matchable (Product :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (Proxy :: Type -> Type) Source # | |
Matchable (Sum :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (V1 :: Type -> Type) Source # | |
Matchable ((:*:) :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (Proxy :: Type -> Type) Source # | |
Matchable ((:+:) :: (Type -> Type) -> (Type -> Type) -> Type -> Type) (V1 :: Type -> Type) Source # | |
splittingNE :: (Matchable t i, FunctorBy t f) => NonEmptyBy t f <~> t f (ListBy t f) Source #
An
is isomorphic to an NonEmptyBy
t ff
consed with an
, like
how a ListBy
t f
is isomorphic to NonEmpty
a(a, [a])
.
matchingLB :: forall t i f. (Matchable t i, FunctorBy t f) => ListBy t f <~> (i :+: NonEmptyBy t f) Source #
An
is isomorphic to either the empty case (ListBy
t fi
) or the
non-empty case (
), like how NonEmptyBy
t f[a]
is isomorphic to
.Maybe
(NonEmpty
a)