packages feed

lol-typing-1.20160822: Language/LOL/Typing/Lib/Control/Monad/Classes/StateFix.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}

-- | 'MonadState' whose state is parameterized by the 'Monad' stack.
module Language.LOL.Typing.Lib.Control.Monad.Classes.StateFix where

import Control.Applicative (Applicative(..))
import Control.Monad
import Control.Monad.Classes
import Control.Monad.Trans.Class
import qualified Control.Monad.Trans.State.Lazy as SL
-- import qualified Control.Monad.Trans.State.Strict as SS -- TODO: when needed :)
import Data.Bool (Bool(..))
import Data.Function ((.), const)
import Data.Functor.Identity (Identity)
import GHC.Prim (Proxy#, proxy#)
import Prelude (seq)

import Language.LOL.Typing.Lib.Control.Monad.Classes.EffectsFix

-- * Type 'StateLazyFixT'

data StateLazyFixT
 (st::{-StateLazyFixT st m-}(* -> *) -> *)
 (m::{-a-}* -> *)
 (a:: *)
 =   StateLazyFixT
 { unStateLazyFixT :: SL.StateT (st (StateLazyFixT st m)) m a }
 deriving (Functor)
instance Monad m => Applicative (StateLazyFixT st m) where
	pure = return
	(<*>) = ap
instance Monad m => Monad (StateLazyFixT st m) where
	return  = StateLazyFixT . return
	m >>= f = StateLazyFixT (unStateLazyFixT m >>= unStateLazyFixT . f)
instance MonadTrans (StateLazyFixT st) where
	lift = StateLazyFixT . lift

-- ** Type 'StateLazyFix'
type StateLazyFix  st
 =   StateLazyFixT st Identity

-- * Type family 'StateFixCanDo'

type instance CanDo (StateLazyFixT s m) eff
 = StateFixCanDo s eff

type family StateFixCanDo s eff where
	StateFixCanDo s (EffStateFix s)  = 'True
	StateFixCanDo s (EffReaderFix s) = 'True
	StateFixCanDo s (EffLocalFix s)  = 'True
	StateFixCanDo s (EffWriterFix s) = 'True
	StateFixCanDo s eff              = 'False

-- * Class 'MonadStateFixN'

class Monad m => MonadStateFixN (n :: Peano) s m where
	stateFixN :: Proxy# n -> (s m -> (a, s m)) -> m a

-- | Warning: only work when 'StateLazyFixT'
-- is the outermost 'Monad' (i.e. when @n@ @~@ 'Zero'),
-- because the state is paramaterized by this 'Monad'.
instance Monad m => MonadStateFixN 'Zero s (StateLazyFixT s m) where
	stateFixN _ = StateLazyFixT . SL.state

-- ** Type 'MonadStateFixN'

-- | The @'MonadStateFix' s m@ constraint asserts that @m@ is a 'Monad' stack
-- that supports state operations on type @s@
type MonadStateFix (s::(* -> *) -> *) m
 =   MonadStateFixN (Find (EffStateFix s) m) s m

-- | Construct a state 'Monad' computation from a function
stateFix
 :: forall s m a. (MonadStateFix s m)
 => (s m -> (a, s m)) -> m a
stateFix = stateFixN (proxy# :: Proxy# (Find (EffStateFix s) m))

-- | @'put' s@ sets the state within the 'Monad' to @s@
putFix :: MonadStateFix s m => s m -> m ()
putFix s = stateFix (const ((), s))

-- | Fetch the current value of the state within the 'Monad'
getFix :: MonadStateFix s m => m (s m)
getFix = stateFix (\s -> (s, s))

-- | Gets specific component of the state,
-- using a projection function supplied.
getsFix :: MonadStateFix s m => (s m -> a) -> m a
getsFix f = do
	s <- getFix
	return (f s)

-- | Maps an old state to a new state inside a state 'Monad' layer
modifyFix :: MonadStateFix s m => (s m -> s m) -> m ()
modifyFix f = stateFix (\s -> ((), f s))

-- | A variant of 'modify' in which the computation
-- is strict in the new state.
modifyFix' :: MonadStateFix s m => (s m -> s m) -> m ()
modifyFix' f = stateFix (\s -> let s' = f s in s' `seq` ((), s'))

-- Return the 'Monad' parameter and the state.
runStateLazyFix
 :: st (StateLazyFixT st m)
 ->     StateLazyFixT st m  a
 ->                      m (a, st (StateLazyFixT st m))
runStateLazyFix s m = SL.runStateT (unStateLazyFixT m) s

-- Return the 'Monad' parameter.
evalStateLazyFix
 :: Monad m
 => st (StateLazyFixT st m)
 ->     StateLazyFixT st m a
 ->                      m a
evalStateLazyFix s m = SL.evalStateT (unStateLazyFixT m) s

-- Return the state.
execStateLazyFix
 :: Monad m
 => st (StateLazyFixT st m)
 ->     StateLazyFixT st m a
 ->                      m (st (StateLazyFixT st m))
execStateLazyFix s m = SL.execStateT (unStateLazyFixT m) s