MyNixOS website logo
Description

FFunctor: functors on (the usual) Functors.

FFunctor is a type class for endofunctors on the category of Functors.

FMonad is a type class for monads in the category of Functors.

functor-monad: Monads on category of Functors

This package provides FFunctor and FMonad, each corresponds to Functor and Monad but higher-order.

a Functor fa FFunctor ff
Takesa :: Typeg :: Type -> Type, Functor g
Makesf a :: Typeff g :: Type -> Type, Functor (ff g)
Methodsfmap :: (a -> b) -> f a -> f bffmap :: (Functor g, Functor h) => (g ~> h) -> (ff g ~> ff h)
a Monad ma FMonad mm
SuperclassFunctorFFunctor
Methodsreturn = pure :: a -> m afpure :: (Functor g) => g ~> mm g
(=<<) :: (a -> m b) -> m a -> m bfbind :: (Functor g, Functor h) => (g ~> mm h) -> (mm g ~> mm h)
join :: m (m a) -> m afjoin :: (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 Functors, 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 in hkd 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 of Monads and monad homomorphisms. If T is a MFunctor, it takes a Monad m and constructs Monad (T m), and its hoist 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 Functors and natural transformations, which are similar but definitely distinct concept.

    For example, Sum f in the example above is not an instance of MFunctor, since there are no general way to make Sum f m a Monad for arbitrary Monad 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 functions t :: forall (x :: k). f x -> g x.

    While any instance of FFunctor from this package can be faithfully represented as a IFunctor, some instances can't be an instance of IFunctoras is. Most notably, Free can't be an instance of IFunctor directly, because Free needs Functor h to be able to implement fmapI, the method of IFunctor.

    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 require Functor f to be a Functor itself, for example Program from operational package.

    This package avoids the neccesity of the workaround by admitting the restriction that the parameter of FFunctor must always be a Functor. Therefore, FFunctor gives up instances which don't take Functor parameter, for example, a type constructor F with kind F :: (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: some FFunctor instances can only be HFunctor via alternative representations. Same applies for FMonad versus Inject + HBind.

Metadata

Version

0.1.1.0

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows