blob: 73227724415b6c7667b8d041385f56d0a3c3fb1b [file] [log] [blame] [edit]
-- |
-- Module : Minigent.Fresh
-- Copyright : (c) Data61 2018-2019
-- Commonwealth Science and Research Organisation (CSIRO)
-- ABN 41 687 119 230
-- License : BSD3
--
-- This module provides functionality for /fresh variable/ generation, for use in type inference, constraint
-- solving, code generation or elsewhere.
--
-- It exports a @transformers@-style /monad transformer/ 'FreshT', as well as an @mtl@ style
-- type class 'MonadFresh'. An alias 'Fresh' is also provided for cases where a full-blown
-- monad transformer is not needed.
--
-- It expects to be imported unqualified.
{-# LANGUAGE FunctionalDependencies, KindSignatures, FlexibleInstances,
GeneralizedNewtypeDeriving, UndecidableInstances
#-}
module Minigent.Fresh
( -- * Monad Transformer
FreshT
, MonadFresh(..)
, runFreshT
, evalFreshT
-- * Plain Monad
, Fresh(..)
, runFresh
, evalFresh
-- * Utility Functions
, freshes
) where
import qualified Data.Stream as S
import Control.Applicative
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Maybe
import Control.Monad.Fix
import Control.Monad.IO.Class
import Control.Monad.Cont.Class
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State.Strict
import Control.Monad.Identity
-- | A @FreshT s m a@ is a monadic action that, on top of the underlying monad @m@, also may create
-- fresh values of type @s@, ultimately returning a value of type @a@.
--
-- Internally implemented as a state monad over an infinite 'S.Stream' of (assumed) unique values.
newtype FreshT s m a = FreshT (StateT (S.Stream s) m a)
deriving ( Monad, Applicative, Functor
, Alternative, MonadPlus
, MonadFix, MonadIO, MonadTrans
, MonadCont, MonadError e
, MonadReader r, MonadWriter w
)
instance MonadState x m => MonadState x (FreshT s m) where
get = FreshT (lift get)
put x = FreshT (lift $ put x)
instance (Monad m, MonadFresh x m) => MonadFresh x (MaybeT m) where
fresh = lift fresh
instance (Monad m, MonadFresh x m) => MonadFresh x (StateT s m) where
fresh = lift fresh
instance (Monad m, MonadFresh x m) => MonadFresh x (ReaderT s m) where
fresh = lift fresh
instance (Monad m, MonadFresh x m) => MonadFresh x (ExceptT s m) where
fresh = lift fresh
instance (Monad m, Monoid s, MonadFresh x m) => MonadFresh x (WriterT s m) where
fresh = lift fresh
instance Monad m => MonadFresh s (FreshT s m) where
fresh = FreshT (state (\s -> (S.head s, S.tail s)))
-- | Any monad @m@ that is an instance of @MonadFresh s m@ may call the 'fresh' function, which will
-- return a unique value of type @s@.
--
-- Any transformer stack with a 'FreshT' somewhere in it is probably an instance of 'MonadFresh'
-- automatically.
class MonadFresh s (m :: * -> *) | m -> s where
-- | Returns a unique value of type @s@ in the monad @m@.
fresh :: m s
-- | Given an input 'S.Stream' of values, run a 'FreshT' computation, additionally returning the
-- remaining unused fresh values.
--
-- The given stream must never cycle or repeat values. That is, for a stream @s@ and indices
-- @m@ and @n@, @s ! m == s ! n@ if an only if @m == n@.
runFreshT :: Monad m => S.Stream s -> FreshT s m a -> m (a, S.Stream s)
runFreshT stream (FreshT st) = runStateT st stream
-- | Given an input 'S.Stream' of values, run a 'FreshT' computation, only returning the
-- result of the computation.
--
-- The given stream must never cycle or repeat values. That is, for a stream @s@ and indices
-- @m@ and @n@, @s ! m == s ! n@ if an only if @m == n@.
evalFreshT :: Monad m => S.Stream s -> FreshT s m a -> m a
evalFreshT stream (FreshT st) = evalStateT st stream
-- | An alias for convenience, where monad transformers are not required.
type Fresh s = FreshT s Identity
-- | Given an input 'S.Stream' of values, run a 'Fresh' computation, additionally returning the
-- remaining unused fresh values.
--
-- The given stream must never cycle or repeat values. That is, for a stream @s@ and indices
-- @m@ and @n@, @s ! m == s ! n@ if an only if @m == n@.
runFresh :: S.Stream s -> Fresh s a -> (a, S.Stream s)
runFresh stream = runIdentity . runFreshT stream
-- | Given an input 'S.Stream' of values, run a 'Fresh' computation, only returning the
-- result of the computation.
--
-- The given stream must never cycle or repeat values. That is, for a stream @s@ and indices
-- @m@ and @n@, @s ! m == s ! n@ if an only if @m == n@.
evalFresh :: S.Stream s -> Fresh s a -> a
evalFresh stream = runIdentity . evalFreshT stream
-- | @freshes n@ is equivalent to calling 'fresh' @n@ times, returning the results in a list.
freshes :: (Monad m, MonadFresh x m) => Int -> m [x]
freshes 0 = pure []
freshes n = (:) <$> fresh <*> freshes (n-1)