Copyright | (c) Justin Le 2019 |
---|---|
License | BSD3 |
Maintainer | justin@jle.im |
Stability | experimental |
Portability | non-portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides abstractions for working with unary functor combinators.
Principally, it defines the HFunctor
itself, as well as some classes
that expose extra functionality that some HFunctor
s have (Inject
and
HBind
).
See Data.HFunctor.Interpret for tools to use HFunctor
s as functor
combinators that can represent interpretable schemas, and
Data.HBifunctor for an abstraction over binary functor combinators.
Synopsis
- class HFunctor t where
- overHFunctor :: HFunctor t => (f <~> g) -> t f <~> t g
- class HFunctor t => Inject t where
- class Inject t => HBind t where
- data ProxyF f a = ProxyF
- data ConstF e f a = ConstF {
- getConstF :: e
- data HLift t f a
- retractHLift :: Inject t => HLift t f a -> t f a
- data HFree t f a
- foldHFree :: forall t f g. HFunctor t => (f ~> g) -> (t g ~> g) -> HFree t f ~> g
- retractHFree :: HBind t => HFree t f a -> t f a
Documentation
class HFunctor t where Source #
An HFunctor
can be thought of a unary "functor transformer" ---
a basic functor combinator. It takes a functor as input and returns
a functor as output.
It "enhances" a functor with extra structure (sort of like how a monad
transformer enhances a Monad
with extra structure).
As a uniform inteface, we can "swap the underlying functor" (also
sometimes called "hoisting"). This is what hmap
does: it lets us swap
out the f
in a t f
for a t g
.
For example, the free monad Free
takes a Functor
and returns a new
Functor
. In the process, it provides a monadic structure over f
.
hmap
lets us turn a
into a Free
f
: a monad built over
Free
gf
can be turned into a monad built over g
.
For the ability to move in and out of the enhanced functor, see
Inject
and Interpret
.
This class is similar to MFunctor
from
Control.Monad.Morph, but instances must work without a Monad
constraint.
This class is also found in the hschema library with the same name.
hmap :: (f ~> g) -> t f ~> t g Source #
If we can turn an f
into a g
, then we can turn a t f
into
a t g
.
It must be the case that
hmap
id
== id
Essentially, t f
adds some "extra structure" to f
. hmap
must swap out the functor, without affecting the added structure.
For example,
is essentially a list of ListF
f af a
s. If we
hmap
to swap out the f a
s for g a
s, then we must ensure that
the "added structure" (here, the number of items in the list, and
the ordering of those items) remains the same. So, hmap
must
preserve the number of items in the list, and must maintain the
ordering.
The law
is a way of formalizing this property.hmap
id
== id
Instances
overHFunctor :: HFunctor t => (f <~> g) -> t f <~> t g Source #
Lift an isomorphism over an HFunctor
.
Essentailly, if f
and g
are isomorphic, then so are t f
and t g
.
class HFunctor t => Inject t where Source #
A typeclass for HFunctor
s where you can "inject" an f a
into a t
f a
:
inject
:: f a -> t f a
If you think of t f a
as an "enhanced f
", then inject
allows you
to use an f
as its enhanced form.
With the exception of directly pattern matching on the result, inject
itself is not too useful in the general case without
Interpret
to allow us to interpret or retrieve
back the f
.
Lift from f
into the enhanced t f
structure. Analogous to
lift
from MonadTrans
.
Note that this lets us "lift" a f a
; if you want to lift an a
with a -> t f a
, check if t f
is an instance of Applicative
or
Pointed
.
Instances
class Inject t => HBind t where Source #
HBind
is effectively a "higher-order Monad
", in the sense that
HFunctor
is a "higher-order Functor
".
It can be considered a typeclass for HFunctor
s that you can bind
continuations to, nautraluniversal over all f
functors. They work
"for all functors" you lift, without requiring any constraints.
It is very similar to Interpret
, except
Interpret
has the ability to constrain the
contexts to some typeclass.
The main law is that binding inject
should leave things unchanged:
hbind
inject
==id
But hbind
should also be associatiatve, in a way that makes
hjoin
. hjoin = hjoin .hmap
hjoin
That is, squishing a t (t (t f)) a
into a t f a
can be done "inside"
first, then "outside", or "outside" first, then "inside".
Note that these laws are different from the
Interpret
laws, so we often have instances
where hbind
and interpret
(though they both
may typecheck) produce different behavior.
This class is similar to MMonad
from
Control.Monad.Morph, but instances must work without a Monad
constraint.
hbind :: (f ~> t g) -> t f ~> t g Source #
Bind a continuation to a t f
into some context g
.
hjoin :: t (t f) ~> t f Source #
Collapse a nested t (t f)
into a single t f
.
Instances
Simple instances
The functor combinator that forgets all structure in the input. Ignores the input structure and stores no information.
Acts like the "zero" with respect to functor combinator composition.
ComposeT
ProxyF f ~ ProxyFComposeT
f ProxyF ~ ProxyF
It can be inject
ed into (losing all information), but it is impossible
to ever retract
or
interpret
it.
This is essentially
.ConstF
()
Instances
HFunctor (ProxyF :: (k1 -> Type) -> k2 -> Type) Source # | |
HBind (ProxyF :: (k -> Type) -> k -> Type) Source # | |
Inject (ProxyF :: (k -> Type) -> k -> Type) Source # | |
Interpret (ProxyF :: (Type -> Type) -> Type -> Type) Source # | The only way for this to obey |
Functor (ProxyF f :: Type -> Type) Source # | |
Foldable (ProxyF f :: Type -> Type) Source # | |
Defined in Data.HFunctor fold :: Monoid m => ProxyF f m -> m # foldMap :: Monoid m => (a -> m) -> ProxyF f a -> m # foldr :: (a -> b -> b) -> b -> ProxyF f a -> b # foldr' :: (a -> b -> b) -> b -> ProxyF f a -> b # foldl :: (b -> a -> b) -> b -> ProxyF f a -> b # foldl' :: (b -> a -> b) -> b -> ProxyF f a -> b # foldr1 :: (a -> a -> a) -> ProxyF f a -> a # foldl1 :: (a -> a -> a) -> ProxyF f a -> a # elem :: Eq a => a -> ProxyF f a -> Bool # maximum :: Ord a => ProxyF f a -> a # minimum :: Ord a => ProxyF f a -> a # | |
Traversable (ProxyF f :: Type -> Type) Source # | |
Eq1 (ProxyF f :: Type -> Type) Source # | |
Ord1 (ProxyF f :: Type -> Type) Source # | |
Defined in Data.HFunctor | |
Read1 (ProxyF f :: Type -> Type) Source # | |
Defined in Data.HFunctor | |
Show1 (ProxyF f :: Type -> Type) Source # | |
Eq (ProxyF f a) Source # | |
(Typeable f, Typeable a, Typeable k1, Typeable k2) => Data (ProxyF f a) Source # | |
Defined in Data.HFunctor gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> ProxyF f a -> c (ProxyF f a) # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (ProxyF f a) # toConstr :: ProxyF f a -> Constr # dataTypeOf :: ProxyF f a -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c (ProxyF f a)) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c (ProxyF f a)) # gmapT :: (forall b. Data b => b -> b) -> ProxyF f a -> ProxyF f a # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> ProxyF f a -> r # gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> ProxyF f a -> r # gmapQ :: (forall d. Data d => d -> u) -> ProxyF f a -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> ProxyF f a -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> ProxyF f a -> m (ProxyF f a) # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> ProxyF f a -> m (ProxyF f a) # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> ProxyF f a -> m (ProxyF f a) # | |
Ord (ProxyF f a) Source # | |
Read (ProxyF f a) Source # | |
Show (ProxyF f a) Source # | |
Generic (ProxyF f a) Source # | |
type C (ProxyF :: (Type -> Type) -> Type -> Type) Source # | |
Defined in Data.HFunctor.Interpret | |
type Rep (ProxyF f a) Source # | |
Functor combinator that forgets all structure on the input, and
instead stores a value of type e
.
Like ProxyF
, acts like a "zero" with functor combinator composition.
It can be inject
ed into (losing all information), but it is impossible
to ever retract
or
interpret
it.
Instances
HFunctor (ConstF e :: (k1 -> Type) -> k2 -> Type) Source # | |
Monoid e => Inject (ConstF e :: (k -> Type) -> k -> Type) Source # | |
Monoid e => Interpret (ConstF e :: (Type -> Type) -> Type -> Type) Source # | The only way for this to obey |
Functor (ConstF e f :: Type -> Type) Source # | |
Foldable (ConstF e f :: Type -> Type) Source # | |
Defined in Data.HFunctor fold :: Monoid m => ConstF e f m -> m # foldMap :: Monoid m => (a -> m) -> ConstF e f a -> m # foldr :: (a -> b -> b) -> b -> ConstF e f a -> b # foldr' :: (a -> b -> b) -> b -> ConstF e f a -> b # foldl :: (b -> a -> b) -> b -> ConstF e f a -> b # foldl' :: (b -> a -> b) -> b -> ConstF e f a -> b # foldr1 :: (a -> a -> a) -> ConstF e f a -> a # foldl1 :: (a -> a -> a) -> ConstF e f a -> a # toList :: ConstF e f a -> [a] # null :: ConstF e f a -> Bool # length :: ConstF e f a -> Int # elem :: Eq a => a -> ConstF e f a -> Bool # maximum :: Ord a => ConstF e f a -> a # minimum :: Ord a => ConstF e f a -> a # | |
Traversable (ConstF e f :: Type -> Type) Source # | |
Defined in Data.HFunctor | |
Eq e => Eq1 (ConstF e f :: Type -> Type) Source # | |
Ord e => Ord1 (ConstF e f :: Type -> Type) Source # | |
Defined in Data.HFunctor | |
Read e => Read1 (ConstF e f :: Type -> Type) Source # | |
Defined in Data.HFunctor | |
Show e => Show1 (ConstF e f :: Type -> Type) Source # | |
Eq e => Eq (ConstF e f a) Source # | |
(Typeable f, Typeable a, Typeable k1, Typeable k2, Data e) => Data (ConstF e f a) Source # | |
Defined in Data.HFunctor gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> ConstF e f a -> c (ConstF e f a) # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (ConstF e f a) # toConstr :: ConstF e f a -> Constr # dataTypeOf :: ConstF e f a -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c (ConstF e f a)) # dataCast2 :: Typeable t => (forall d e0. (Data d, Data e0) => c (t d e0)) -> Maybe (c (ConstF e f a)) # gmapT :: (forall b. Data b => b -> b) -> ConstF e f a -> ConstF e f a # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> ConstF e f a -> r # gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> ConstF e f a -> r # gmapQ :: (forall d. Data d => d -> u) -> ConstF e f a -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> ConstF e f a -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> ConstF e f a -> m (ConstF e f a) # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> ConstF e f a -> m (ConstF e f a) # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> ConstF e f a -> m (ConstF e f a) # | |
Ord e => Ord (ConstF e f a) Source # | |
Defined in Data.HFunctor | |
Read e => Read (ConstF e f a) Source # | |
Show e => Show (ConstF e f a) Source # | |
Generic (ConstF e f a) Source # | |
type C (ConstF e :: (Type -> Type) -> Type -> Type) Source # | |
Defined in Data.HFunctor.Interpret | |
type Rep (ConstF e f a) Source # | |
Defined in Data.HFunctor |
HFunctor
Combinators
An "HFunctor
combinator" that enhances an HFunctor
with the
ability to hold a single f a
. This is the higher-order analogue of
Lift
.
You can think of it as a free Inject
for any f
.
Instances
HFunctor t => HFunctor (HLift t :: (k -> Type) -> k -> Type) Source # | |
(HBind t, Inject t) => HBind (HLift t :: (k -> Type) -> k -> Type) Source # | |
HFunctor t => Inject (HLift t :: (k -> Type) -> k -> Type) Source # | |
Interpret t => Interpret (HLift t) Source # | Never uses |
(Functor f, Functor (t f)) => Functor (HLift t f) Source # | |
(Eq1 (t f), Eq1 f) => Eq1 (HLift t f) Source # | |
(Ord1 (t f), Ord1 f) => Ord1 (HLift t f) Source # | |
Defined in Data.HFunctor | |
(Show1 (t f), Show1 f) => Show1 (HLift t f) Source # | |
(Eq (f a), Eq (t f a)) => Eq (HLift t f a) Source # | |
(Ord (f a), Ord (t f a)) => Ord (HLift t f a) Source # | |
Defined in Data.HFunctor | |
(Read (f a), Read (t f a)) => Read (HLift t f a) Source # | |
(Show (f a), Show (t f a)) => Show (HLift t f a) Source # | |
type C (HLift t) Source # | |
Defined in Data.HFunctor.Interpret |
retractHLift :: Inject t => HLift t f a -> t f a Source #
An "HFunctor
combinator" that turns an HFunctor
into potentially
infinite nestings of that HFunctor
.
An
is either HFree
t f af a
, t f a
, t (t f) a
, t (t (t f))
a
, etc.
This effectively turns t
into a tree with t
branches.
One particularly useful usage is with MapF
. For example if you had
a data type representing a command line command parser:
data Command a
You could represent "many possible named commands" using
type Commands =MapF
String
Command
And you can represent multiple nested named commands using:
type NestedCommands =HFree
(MapF
String
)
This has an Interpret
instance, but it can be
more useful to use via direct pattern matching, or through
foldHFree
::HBifunctor
t => f~>
g -> t g ~> g -> HFree t f ~> g
which requires no extra constriant on g
, and lets you consider each
branch separately.
This can be considered the higher-oder analogue of
Free
; it is the free HBind
for any
.HFunctor
t
Instances
HFunctor t => HFunctor (HFree t :: (k -> Type) -> k -> Type) Source # | |
HFunctor t => HBind (HFree t :: (k -> Type) -> k -> Type) Source # | |
HFunctor t => Inject (HFree t :: (k -> Type) -> k -> Type) Source # | |
Interpret t => Interpret (HFree t) Source # | Never uses |
(Functor f, Functor (t (HFree t f))) => Functor (HFree t f) Source # | |
(Show1 (t (HFree t f)), Show1 f) => Show1 (HFree t f) Source # | |
(Show1 (t (HFree t f)), Show1 f, Show a) => Show (HFree t f a) Source # | |
type C (HFree t) Source # | |
Defined in Data.HFunctor.Interpret |