FFunctor: functors on (the usual) Functors.
FFunctor
is a type class for endofunctors on the category of Functor
s.
FMonad
is a type class for monads in the category of Functor
s.
functor-monad: Monads on category of Functors
This package provides FFunctor
and FMonad
, each corresponds to Functor
and Monad
but higher-order.
a Functor f | a FFunctor ff | |
---|---|---|
Takes | a :: Type | g :: Type -> Type , Functor g |
Makes | f a :: Type | ff g :: Type -> Type , Functor (ff g) |
Methods | fmap :: (a -> b) -> f a -> f b | ffmap :: (Functor g, Functor h) => (g ~> h) -> (ff g ~> ff h) |
a Monad m | a FMonad mm | |
---|---|---|
Superclass | Functor | FFunctor |
Methods | return = pure :: a -> m a | fpure :: (Functor g) => g ~> mm g |
(=<<) :: (a -> m b) -> m a -> m b | fbind :: (Functor g, Functor h) => (g ~> mm h) -> (mm g ~> mm h) | |
join :: m (m a) -> m a | fjoin :: (Functor g) => mm (mm g) ~> mm g |
See also: https://viercc.github.io/blog/posts/2020-08-23-fmonad.html (Japanese article)
Motivational examples
Many types defined in base and popolar libraries like transformers take a parameter expecting a Functor
. Here are two, simple examples.
-- From "base", Data.Functor.Sum
data Sum f g x = InL (f x) | InR (g x)
instance (Functor f, Functor g) => Functor (Sum f g)
-- From "transformers", Control.Monad.Trans.Reader
newtype ReaderT r m x = ReaderT { runReaderT :: r -> m x }
instance (Functor m) => Functor (ReaderT r m)
These types often have a way to map a natural transformation, an arrow between two Functor
s, over that parameter.
-- The type of natural transformations
type (~>) f g = forall x. f x -> g x
mapRight :: (g ~> g') -> Sum f g ~> Sum f g'
mapRight _ (InL fx) = InL fx
mapRight nt (InR gx) = InR (nt gx)
mapReaderT :: (m a -> n b) -> ReaderT r m a -> ReaderT r n b
-- mapReaderT can be used to map natural transformation
mapReaderT' :: (m ~> n) -> (ReaderT r m ~> ReaderT r n)
mapReaderT' naturalTrans = mapReaderT naturalTrans
The type class FFunctor
abstracts type constructors equipped with such a function.
class (forall g. Functor g => Functor (ff g)) => FFunctor ff where
ffmap :: (Functor g, Functor h) => (g ~> h) -> ff g x -> ff h x
ffmap :: (Functor g, Functor g') => (g ~> g') -> Sum f g x -> Sum f g' x
ffmap :: (Functor m, Functor n) => (m ~> n) -> ReaderT r m x -> ReaderT r n x
Not all but many FFunctor
instances can, in addition to ffmap
, support Monad
-like operations. This package provide another type class FMonad
to represent such operations.
class (FFunctor mm) => FMonad mm where
fpure :: (Functor g) => g ~> mm g
fbind :: (Functor g, Functor h) => (g ~> ff h) -> ff g a -> ff h a
Both of the above examples, Sum
and ReaderT r
, have FMonad
instances.
instance Functor f => FMonad (Sum f) where
fpure :: (Functor g) => g ~> Sum f g
fpure = InR
fbind :: (Functor g, Functor h) => (g ~> Sum f h) -> Sum f g a -> Sum f h a
fbind _ (InL fa) = InL fa
fbind k (InR ga) = k ga
instance FMonad (ReaderT r) where
fpure :: (Functor m) => m ~> ReaderT r m
-- return :: x -> (e -> x)
fpure = ReaderT . return
fbind :: (Functor m, Functor n) => (m ~> ReaderT r n) -> ReaderT r m a -> ReaderT r n a
-- join :: (e -> e -> x) -> (e -> x)
fbind k = ReaderT . (>>= runReaderT . k) . runReaderT
Comparison against similar type classes
There are packages with very similar type classes, but they are more or less different to this package.
From hkd:
FFunctor
There is a class named
FFunctor
inhkd
package too. It represents a functor from /category of type constructors/k -> Type
to the category of usual types and functions.Since it is not an endofunctor, there can be no
Monad
-like classes on them.Another package rank2classes also provides the same class
Rank2.Functor
.From mmorph:
MFunctor
,MMonad
MFunctor
is a class for endofunctors on the category ofMonad
s and monad homomorphisms. IfT
is aMFunctor
, it takes aMonad m
and constructsMonad (T m)
, and itshoist
method takes a Monad morphismm ~> n
and returns a new Monad morphismT m ~> T n
.On the other hand, this library is about endofunctors on the category of
Functor
s and natural transformations, which are similar but definitely distinct concept.For example,
Sum f
in the example above is not an instance ofMFunctor
, since there are no general way to makeSum f m
aMonad
for arbitraryMonad m
.instance Functor f => FFunctor (Sum f) instance Functor f => MFunctor (Sum f) -- Can be written, but it will violate the requirement to be MFunctor
From index-core:
IFunctor
,IMonad
They are endofunctors on the category of type constructors of kind
k -> Type
and polymorphic functionst :: forall (x :: k). f x -> g x
.While any instance of
FFunctor
from this package can be faithfully represented as aIFunctor
, some instances can't be an instance ofIFunctor
as is. Most notably, Free can't be an instance ofIFunctor
directly, becauseFree
needsFunctor h
to be able to implementfmapI
, the method ofIFunctor
.class IFunctor ff where fmapI :: (g ~> h) -> (ff g ~> ff h)
There exists a workaround: you can use another representation of
Free f
which doesn't requireFunctor f
to be aFunctor
itself, for exampleProgram
from operational package.This package avoids the neccesity of the workaround by admitting the restriction that the parameter of
FFunctor
must always be aFunctor
. Therefore,FFunctor
gives up instances which don't takeFunctor
parameter, for example, a type constructorF
with kindF :: (Nat -> Type) -> Nat -> Type
.From functor-combinators:
HFunctor
,Inject
,HBind
This package can be thought of as a more developed version of
index-core
, since they share the base assumption. The tradeoff between this package is the same: someFFunctor
instances can only beHFunctor
via alternative representations. Same applies forFMonad
versusInject + HBind
.