hobbits-1.3: src/Data/Binding/Hobbits/MonadBind.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
-- |
-- Module : Data.Binding.Hobbits.MonadBind
-- Copyright : (c) 2020 Edwin Westbrook
--
-- License : BSD3
--
-- Maintainer : westbrook@galois.com
-- Stability : experimental
-- Portability : GHC
--
-- This module defines monads that are compatible with the notion of
-- name-binding, where a monad is compatible with name-binding iff it can
-- intuitively run computations that are inside name-bindings. More formally, a
-- /binding monad/ is a monad with an operation 'mbM' that commutes name-binding
-- with the monadic operations, meaning:
--
-- > 'mbM' ('nuMulti' $ \ns -> 'return' a) == 'return' ('nuMulti' $ \ns -> a)
-- > 'mbM' ('nuMulti' $ \ns -> m >>= f)
-- > == 'mbM' ('nuMulti' $ \ns -> m) >>= \mb_x ->
-- > 'mbM' (('nuMulti' $ \ns x -> f x) `'mbApply'` mb_x)
module Data.Binding.Hobbits.MonadBind (MonadBind(..), MonadStrongBind(..)) where
import Data.Binding.Hobbits.Closed
import Data.Binding.Hobbits.Liftable (mbLift)
import Data.Binding.Hobbits.Mb
import Data.Binding.Hobbits.NuMatching
import Data.Binding.Hobbits.QQ
import Control.Monad.Identity (Identity(..))
import Control.Monad.Reader (ReaderT(..))
import Control.Monad.State (StateT(..), get, lift, put, runStateT)
-- | The class of name-binding monads
class Monad m => MonadBind m where
mbM :: NuMatching a => Mb ctx (m a) -> m (Mb ctx a)
-- | Bind a name inside a computation and return the name-binding whose body was
-- returned by the computation
nuM :: (MonadBind m, NuMatching b) => (Name a -> m b) -> m (Binding a b)
nuM = mbM . nu
instance MonadBind Identity where
mbM = Identity . fmap runIdentity
instance MonadBind Maybe where
mbM [nuP| Just x |] = return x
mbM [nuP| Nothing |] = Nothing
instance MonadBind m => MonadBind (ReaderT r m) where
mbM mb = ReaderT $ \r -> mbM $ fmap (flip runReaderT r) mb
-- | A version of 'MonadBind' that does not require a 'NuMatching' instance on
-- the element type of the multi-binding in the monad
class MonadBind m => MonadStrongBind m where
strongMbM :: Mb ctx (m a) -> m (Mb ctx a)
instance MonadStrongBind Identity where
strongMbM = Identity . fmap runIdentity
instance MonadStrongBind m => MonadStrongBind (ReaderT r m) where
strongMbM mb = ReaderT $ \r -> strongMbM $ fmap (flip runReaderT r) mb
-- | State types that can incorporate name-bindings
class NuMatching s => BindState s where
bindState :: Mb ctx s -> s
instance BindState (Closed s) where
bindState = mbLift
instance (MonadBind m, BindState s) => MonadBind (StateT s m) where
mbM mb_m = StateT $ \s ->
mbM (fmap (\m -> runStateT m s) mb_m) >>= \mb_as ->
return (fmap fst mb_as, bindState (fmap snd mb_as))
instance (MonadStrongBind m, BindState s) => MonadStrongBind (StateT s m) where
strongMbM mb_m = StateT $ \s ->
strongMbM (fmap (\m -> runStateT m s) mb_m) >>= \mb_as ->
return (fmap fst mb_as, bindState (fmap snd mb_as))
-- | A monad whose effects are closed
class Monad m => MonadClosed m where
closedM :: Closed (m a) -> m (Closed a)
instance MonadClosed Identity where
closedM = Identity . clApply $(mkClosed [| runIdentity |])
instance (MonadClosed m, Closable s) => MonadClosed (StateT s m) where
closedM clm =
do s <- get
cl_a_s <- lift $ closedM ($(mkClosed [| runStateT |]) `clApply` clm
`clApply` toClosed s)
put (snd $ unClosed cl_a_s)
return ($(mkClosed [| fst |]) `clApply` cl_a_s)