indexed-transformers-0.2.0.0: src/Control/Monad/Trans/Indexed.hs
{- |
Module : Control.Monad.Trans.Indexed
Copyright : (C) 2026 Eitan Chatav
License : BSD 3-Clause License (see the file LICENSE)
Maintainer : Eitan Chatav <eitan.chatav@gmail.com>
Indexed monad transformers.
-}
module Control.Monad.Trans.Indexed
( IxMonadTrans (..)
, Indexed (..)
, (&)
) where
import Control.Category (Category (..))
import Control.Monad
import Control.Monad.Trans
import Data.Function ((&))
import Data.Kind
import Prelude hiding (id, (.))
{- |
An [Atkey indexed monad]
(https://bentnib.org/paramnotions-jfp.pdf)
is a `Functor` [enriched category]
(https://ncatlab.org/nlab/show/enriched+category).
An indexed monad transformer transforms a `Monad` into an indexed monad.
It is a monad and monad transformer when its source and target index
are the same, enabling use of standard @do@ notation in that case.
In the general case, qualified @Indexed.do@ notation can be used,
even if the source and target index are different.
>>> :set -XQualifiedDo
>>> import qualified Control.Monad.Trans.Indexed.Do as Indexed
-}
type IxMonadTrans
:: (k -> k -> (Type -> Type) -> Type -> Type)
-> Constraint
class
( forall i j m. Monad m => Functor (t i j m)
, forall i j m. (i ~ j, Monad m) => Monad (t i j m)
, forall i j. i ~ j => MonadTrans (t i j)
) => IxMonadTrans t where
{-# MINIMAL joinIx | bindIx #-}
{- |
indexed analog of `<*>`
> prop> (<*>) = apIx
-}
apIx
:: Monad m
=> t i j m (x -> y)
-> t j k m x
-> t i k m y
apIx tf tx = bindIx (<$> tx) tf
{- |
indexed analog of `join`
> prop> join = joinIx
> prop> joinIx = bindIx id
-}
joinIx
:: Monad m
=> t i j m (t j k m y)
-> t i k m y
joinIx = bindIx id
{- |
indexed analog of `=<<`
> prop> (=<<) = bindIx
> prop> bindIx f x = joinIx (f <$> x)
> prop> x & bindIx return = x
> prop> x & bindIx f & bindIx g = x & bindIx (f & andThenIx g)
-}
bindIx
:: Monad m
=> (x -> t j k m y)
-> t i j m x
-> t i k m y
bindIx f t = joinIx (f <$> t)
{- |
indexed analog of flipped `>>`
> prop> (>>) = flip thenIx
> prop> return () & thenIx y = y
-}
thenIx
:: Monad m
=> t j k m y
-> t i j m x
-> t i k m y
thenIx ix2 ix1 = ix1 & bindIx (\ _ -> ix2)
{- |
indexed analog of `<=<`
> prop> (<=<) = andThenIx
> prop> andThenIx g f x = bindIx g (f x)
> prop> f & andThenIx return = f
> prop> return & andThenIx f = f
> prop> f & andThenIx g & andThenIx h = f & andThenIx (g & andThenIx h)
-}
andThenIx
:: Monad m
=> (y -> t j k m z)
-> (x -> t i j m y)
-> x -> t i k m z
andThenIx g f x = bindIx g (f x)
{- |
`Indexed` reshuffles the type parameters of an `IxMonadTrans`,
exposing its `Category` instance.
-}
newtype Indexed t m r i j = Indexed {runIndexed :: t i j m r}
instance
( IxMonadTrans t
, Monad m
, Monoid r
) => Category (Indexed t m r) where
id = Indexed (pure mempty)
Indexed g . Indexed f = Indexed $ apIx (fmap (<>) f) g