packages feed

functor-monad-0.1.1.0: README.md

# 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](https://hackage.haskell.org/package/base-4.18.1.0) and popolar libraries like
[transformers](https://hackage.haskell.org/package/transformers-0.6.1.1) take a parameter expecting a `Functor`.
Here are two, simple examples.

```haskell
-- 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.

```haskell
-- 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.

```haskell
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.

```haskell
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.

```haskell
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](https://hackage.haskell.org/package/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](https://hackage.haskell.org/package/rank2classes) also provides the same class `Rank2.Functor`.
  
* From [mmorph](https://hackage.haskell.org/package/mmorph-1.2.0): `MFunctor`, `MMonad`

  `MFunctor` is a class for endofunctors on the category of `Monad`s 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 morphism* `m ~> n` and returns a new *Monad morphism* `T 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 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](https://hackage.haskell.org/package/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 `IFunctor` _as is_.
  Most notably, [Free](https://hackage.haskell.org/package/free-5.1.8/docs/Control-Monad-Free.html#t:Free) can't be an instance of `IFunctor` directly,
  because `Free` needs `Functor h` to be able to implement `fmapI`, the method of `IFunctor`.

  ```haskell
  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](https://hackage.haskell.org/package/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](https://hackage.haskell.org/package/functor-combinators-0.4.1.2): `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`.