spectacle-1.0.0: src/Language/Spectacle/Lang/Internal.hs
{-# LANGUAGE UndecidableInstances #-}
-- | The 'Lang' monad.
--
-- @since 1.0.0
module Language.Spectacle.Lang.Internal
( Lang (Pure, Op, Scoped),
send,
scope,
)
where
import Control.Applicative (Alternative (empty, (<|>)))
import Control.Monad (MonadPlus (mplus, mzero), (>=>))
import Data.Bool (bool)
import Data.Kind (Type)
import GHC.TypeLits (Symbol)
import Data.Ascript (Ascribe)
import Data.Functor.Loom (Loom (Loom), bind, (~>~))
import qualified Data.Functor.Loom as Loom
import Language.Spectacle.Lang.Member (Member (inject, injectS))
import Language.Spectacle.Lang.Op (Op)
import Language.Spectacle.Lang.Scoped (Effect, EffectK, Scoped)
import Language.Spectacle.Syntax.NonDet.Internal (NonDet (Choose, Empty))
-- -------------------------------------------------------------------------------------------------
-- | 'Lang' is a CEK-style interpreter for the set of effects behind Spectacles syntax and is based
-- on Oleg's Eff monad. 'Lang' differs from Eff in it's @ctx@ parameter and its ability to support
-- higher-order effects.
--
-- * The type parameter @ctx@ is a type row associating variable names to their respective types.
-- This includes plain values from the previous frame as well as primed variables in the next frame.
--
-- * The type parameter @effs@ is the set of effects a 'Lang' is capable of performing.
--
-- @since 1.0.0
type Lang :: [Ascribe Symbol Type] -> [EffectK] -> Type -> Type
data Lang ctxt effs a where
Pure ::
a ->
Lang ctxt effs a
Op ::
Op effs a ->
(a -> Lang ctxt effs b) ->
Lang ctxt effs b
Scoped ::
Scoped effs (Lang ctxt effs') a ->
Loom (Lang ctxt effs') (Lang ctxt effs) a b ->
Lang ctxt effs b
-- | Sends a constructor for the effect @eff@ for 'Lang' to handle.
--
-- @since 1.0.0
send :: Member eff effs => eff a -> Lang ctx effs a
send eff = Op (inject eff) pure
{-# INLINE send #-}
-- | Like 'send', but sends a constructor for the 'Effect' instance of @eff@.
--
-- @since 1.0.0
scope :: Member eff effs => Effect eff (Lang ctx effs) a -> Lang ctx effs a
scope eff = Scoped (injectS eff) Loom.identity
{-# INLINE scope #-}
-- | @since 1.0.0
instance Functor (Lang ctx effs) where
fmap f (Pure x) = Pure (f x)
fmap f (Op u k) = Op u (fmap f . k)
fmap f (Scoped u loom) = Scoped u (fmap f loom)
{-# INLINE fmap #-}
-- | @since 1.0.0
instance Applicative (Lang ctx effs) where
pure = Pure
{-# INLINE CONLIKE pure #-}
Pure f <*> m = fmap f m
Op u k <*> m = Op u ((<*> m) . k)
Scoped u (Loom ctx eta) <*> m = Scoped u (Loom ctx ((<*> m) . eta))
{-# INLINE (<*>) #-}
-- | @since 1.0.0
instance Monad (Lang ctx effs) where
Pure x >>= f = f x
Op u k >>= f = Op u (k >=> f)
Scoped u loom >>= f = Scoped u (loom ~>~ bind f)
{-# INLINE (>>=) #-}
-- | @since 1.0.0
instance Member NonDet effs => Alternative (Lang ctx effs) where
empty = send Empty
{-# INLINE empty #-}
a <|> b = send Choose >>= bool b a
{-# INLINE (<|>) #-}
-- | @since 1.0.0
instance Member NonDet effs => MonadPlus (Lang ctx effs) where
mzero = empty
{-# INLINE mzero #-}
mplus = (<|>)
{-# INLINE mplus #-}