stc-lang-1.0.0: src/Control/Monad/Generator.hs
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE CPP #-}
module Control.Monad.Generator
( IsGenerator(..)
, liftIO
, foldlGenerator
, foldlGeneratorT
, foldlGenerator_
, foldlGeneratorT_
, chanToGenerator
, ioReaderToGenerator
, foldableGenerator
, foldableGenerator'
, foldableGenerator''
, foldableGeneratorEval
, listGenerator
, stateToGenerator
, Generator
-- ** A mutable IO generator variable
, GenVar, newGenVar, pull
) where
import Control.Applicative
import Control.Arrow
import Control.Concurrent.Chan
import Control.Concurrent.MVar
import Control.DeepSeq
import Control.Monad.State
import Data.Foldable (foldr')
import Data.Tuple
import qualified GHC.Exts (IsList(..))
-- | A natural transformation
type f ~> g = forall x. f x -> g x
------------------------------------------------------------------
--
-- The generator
--
------------------------------------------------------------------
-- | There are three basic ways to construct this generator, which
-- correspond to the (not exposed) constructors of this
-- type. 1. 'finish' this marks the end of a generator, 2. 'yield' is
-- used to return a value and continue with the computation afterwards
-- and finally using 'liftIO' you can execute any IO action and then
-- continue.
data Generator m a
= Finished
| Yield (Generator m a)
a
| NeedM (m (Generator m a))
-- At first I implemented all of the `Applicative` and `Monad`
-- functions fully, that is to say with all the recursion down the
-- source generator. I was already suspecting a pattern there,
-- because the recursion for both looked very similar, I just couldn't
-- quite figure it out. When suddenly something *magical* happened.
-- I was looking at `Alternative`, because that instance is needed for
-- `MonadComprehensions` and I was again reminded of just how similar
-- `Alternative` is to `Monoid` and I realized that `Generator` is in
-- fact a `Monoid`. The empty element is `Finished` because it can be
-- appended or prepended to any generator without changing its meaning
-- and `mappend` is simply exhausting the first generator first,
-- followed by the second one. After having that realization
-- implementing `Monad` and `Applicative` became really easy because
-- you just simply create a new generator by applying the function and
-- then prepend a recursion of the respective operation (`>>=` or
-- `<*>`)
-- You can see the generator in action by running the examples at the bottom in ghci with `runGenerator`
mappendGen :: Functor m => Generator m a -> Generator m a -> Generator m a
Finished `mappendGen` gen2 = gen2
NeedM sc `mappendGen` gen2 = NeedM $ (`mappendGen` gen2) <$> sc
Yield g v `mappendGen` gen2 = Yield (g `mappendGen` gen2) v
instance Functor m => Monoid (Generator m a) where
mempty = Finished
#if MIN_VERSION_base(4,11,0)
instance Functor m => Semigroup (Generator m a) where
(<>) = mappendGen
#else
mappend = mappendGen
#endif
instance Functor m => Functor (Generator m) where
fmap _ Finished = Finished
fmap f (NeedM m) = NeedM $ fmap (fmap f) m
fmap f (Yield g a) = Yield (fmap f g) (f a)
instance Functor m => Applicative (Generator m) where
pure = Yield Finished
Finished <*> _ = Finished
NeedM m <*> v = NeedM $ (<*> v) <$> m
Yield fg f <*> v = fmap f v `mappend` (fg <*> v)
instance Functor m => Monad (Generator m) where
return = pure
Finished >>= _ = Finished
NeedM m >>= f = NeedM $ (>>= f) <$> m
Yield cont a >>= f = f a `mappend` (cont >>= f)
-- | This is needed to get the Monad comprehensions
instance Functor m => Alternative (Generator m) where
empty = mempty
(<|>) = mappend
-- | IO can be embedded easily
instance MonadIO m => MonadIO (Generator m) where
liftIO = needM . liftIO
instance Monad m => GHC.Exts.IsList (Generator m a) where
type Item (Generator m a) = a
fromList = listGenerator
toList _ = error "toList: need monad to evaluate generator"
foldlGeneratorT ::
(IsGenerator g f, Monad m)
=> (f ~> m)
-> (b -> a -> m b)
-> b
-> g a
-> m b
foldlGeneratorT trans ac = flip go
where
go gen seed' =
trans (step gen) >>=
maybe (pure seed') (\(a, gen') -> go gen' =<< ac seed' a)
foldlGenerator ::
(IsGenerator g m, Monad m) => (b -> a -> m b) -> b -> g a -> m b
foldlGenerator = foldlGeneratorT id
foldlGeneratorT_ ::
(IsGenerator g f, Monad m) => (f ~> m) -> (a -> m ()) -> g a -> m ()
foldlGeneratorT_ trans f = foldlGeneratorT trans (\() a -> f a) ()
foldlGenerator_ :: (IsGenerator g m, Monad m) => (a -> m ()) -> g a -> m ()
foldlGenerator_ = foldlGeneratorT_ id
ioReaderToGenerator :: (IsGenerator g m, Monad g) => m (Maybe a) -> g a
ioReaderToGenerator reader = recur
where
recur = maybe finish (`yield` recur) =<< needM reader
chanToGenerator ::
(MonadIO m, IsGenerator g m, Monad g) => Chan (Maybe a) -> g a
chanToGenerator = ioReaderToGenerator . liftIO . readChan
foldableGenerator :: (Foldable f, IsGenerator g m) => f a -> g a
foldableGenerator = foldr' yield finish
foldableGeneratorEval ::
(Foldable f, IsGenerator g m) => (forall b. a -> b -> b) -> f a -> g a
foldableGeneratorEval eval = foldr (\a rest -> a `eval` yield a rest) finish
foldableGenerator' :: (Foldable f, IsGenerator g m) => f a -> g a
foldableGenerator' = foldableGeneratorEval seq
foldableGenerator'' :: (Foldable f, IsGenerator g m, NFData a) => f a -> g a
foldableGenerator'' = foldableGeneratorEval deepseq
-----------------------------------------------------------------
--
-- Creating generators
--
------------------------------------------------------------------
listGenerator :: IsGenerator g m => [a] -> g a
listGenerator = foldableGenerator
-- | A generator crated with this will run until it returns `Nothing` in which case the generator finishes
stateToGenerator ::
(Monad g, IsGenerator g m) => StateT s m (Maybe a) -> s -> g a
stateToGenerator st s = do
(a, s') <- needM $ runStateT st s
maybe finish (`yield` stateToGenerator st s') a
------------------------------------------------------------------
--
-- Some more fun stuff that can be done with them
--
------------------------------------------------------------------
-- One fun thing we can do in IO is put the generator in a mutable variable and then just pull values from that.
type GenVar a = MVar (Generator IO a)
newGenVar :: Generator IO a -> IO (GenVar a)
newGenVar = newMVar
-- | This pulls a new value from this var (if possible) and updates its state
pull :: GenVar a -> IO (Maybe a)
pull =
flip modifyMVar $
fmap (maybe (Finished, Nothing) (second Just . swap)) . step
------------------------------------------------------------------
--
-- Some examples of comprehensions, composition and state embedding
--
------------------------------------------------------------------
permutations :: Generator IO (Int, Char)
permutations =
[(i, c) | i <- listGenerator [0 .. 9], c <- listGenerator ['a' .. 'f']]
nonReflexivePermutations :: Int -> Generator IO (Int, Int)
nonReflexivePermutations i = [(a, b) | a <- ints, b <- ints, a /= b]
where
ints = listGenerator [0 .. i]
justSomeStuffWithInts :: Generator IO Int
justSomeStuffWithInts =
flip stateToGenerator 0 $ do
s <- get
if s < 100
then do
modify (+ 4)
pure $ Just s
else do
liftIO $ putStrLn "We have reached 100" -- It can do IO as well ;)
pure Nothing
-- and they are all compatible and can be joined together (and depend on each other)
-- Probably dont run this ... it creates a **lot** of output
crazy :: Generator IO (Int, Int, Char)
crazy =
[ (a + b, a * d, c)
| i <- justSomeStuffWithInts
, (b, d) <- nonReflexivePermutations i
, (a, c) <- permutations
]
------------------------------------------------------------------
--
-- A generator interface
--
------------------------------------------------------------------
-- Something I thought of this morning.
-- There could also be a generic interface for generators
-- | A generator @g@ that runs in the monad @m@
class IsGenerator g m | g -> m where
yield :: a -> g a -> g a
finish :: g a
needM :: m a -> g a
isFinished :: g a -> Bool
default isFinished :: Eq (g a) =>
g a -> Bool
isFinished = (== finish)
-- | Run until the generator yields its first value or finishes.
-- Returns the created value and a new generator which represents its updated internal state.
step :: g a -> m (Maybe (a, g a))
-- | Run a generator producing a list of output values
toList :: g a -> m [a]
default toList :: Monad m =>
g a -> m [a]
toList = foldlGenerator (\b a -> pure $ a : b) []
instance Monad m => IsGenerator (Generator m) m where
yield = flip Yield
finish = Finished
needM = NeedM . fmap pure
isFinished Finished = True
isFinished _ = False
step Finished = pure Nothing
step (NeedM ac) = ac >>= step
step (Yield g a) = pure $ Just (a, g)
toList Finished = pure []
toList (NeedM ac) = ac >>= toList
toList (Yield g a) = (a :) <$> toList g