{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}

module Parameterized.Control.Monad
    ( module Parameterized.Control.Applicative
    , PMonad(..)
    , (&>>=)
    , (&>>)
    , (&=<<)
    , (&>=>)
    , (&<=<)
    ) where

import Data.Kind
import Parameterized.Control.Applicative

-- | Parameterized version of Monad.
class PApplicative m t u v => PMonad (m :: k -> Type -> Type) (t :: k) (u :: k) (v :: k) where
    -- | Sequentially compose two actions, passing any value produced by the first as an argument to the second.
    pbind :: PUnary m t a -> (a -> PUnary m u b) -> PUnary m v b

-- | Sequentially compose two actions, passing any value produced by the first as an argument to the second.
(&>>=) :: PMonad m t u v => PUnary m t a -> (a -> PUnary m u b) -> PUnary m v b
(&>>=) = pbind
infixl 1 &>>=
infixl 1 `pbind`

(&>>) :: PMonad m t u v => PUnary m t a -> PUnary m u b -> PUnary m v b
m &>> k = m &>>= \_ -> k
infixl 1 &>>

-- | Same as '&>>=', but with the arguments interchanged.
(&=<<) :: PMonad m t u v => (a -> PUnary m u b) -> PUnary m t a -> PUnary m v b
f &=<< x = x &>>= f
infixr 1 &=<<

-- | Left-to-right Kleisli composition of monads.
(&>=>) :: (PMonad m t u v) => (a -> PUnary m t b) -> (b -> PUnary m u c) -> (a -> PUnary m v c)
f &>=> g = \x -> f x &>>= g
infixr 1 &>=>

-- | Right-to-left Kleisli composition of monads. @('>=>')@, with the arguments flipped.
(&<=<) :: (PMonad m t u v) => (b -> PUnary m u c) -> (a -> PUnary m t b) -> (a -> PUnary m v c)
(&<=<) = flip (&>=>)
infixr 1 &<=<

-- class (PAlternative m t u v, PMonad m t u v) => PMonadPlus m t u v

-- class PMZero (m :: k -> Type -> Type) where
--     pmezero :: m (PId m) a