packages feed

creditmonad-1.0.0: src/Test/Credit/Queue/Streams.hs

{-# LANGUAGE GADTs, LambdaCase #-}

module Test.Credit.Queue.Streams (Stream(..), SThunk, SLazyCon(..), smatch, credit, evalone, toList, ifIndirect, test) where

import Control.Monad
import Control.Monad.Credit

data Stream m a
  = SCons a (Stream m a)
  | SNil
  | SIndirect (SThunk m (Stream m a))

type SThunk m = Thunk m (SLazyCon m)

data SLazyCon m a where
  SAppend :: Stream m a -> Stream m a -> SLazyCon m (Stream m a)
  SReverse :: Stream m a -> Stream m a -> SLazyCon m (Stream m a)

instance MonadInherit m => HasStep (SLazyCon m) m where
  step (SAppend xs ys) = sappend xs ys
  step (SReverse xs ys) = sreverse xs ys

-- | Smart destructor for streams, consuming one credit
smatch :: MonadInherit m => Stream m a -- ^ Scrutinee
       -> (a -> Stream m a -> m b) -- ^ Cons case
       -> m b -- ^ Nil case
       -> m b
smatch x cons nil = tick >> eval x
  where
    eval x = case x of
      SCons a as -> cons a as
      SNil -> nil
      SIndirect i -> force i >>= eval

-- | delay a computation, consuming all credits
taildelay :: MonadInherit m => SLazyCon m (Stream m a) -> m (Stream m a)
taildelay t = do
  x <- delay t
  creditAllTo x
  pure (SIndirect x)

sreverse :: MonadInherit m => Stream m a -> Stream m a -> m (Stream m a)
sreverse xs ys = smatch xs
  (\x xs -> taildelay (SReverse xs (SCons x ys)))
  (pure ys)

ifIndirect :: Monad m => Stream m a -> (SThunk m (Stream m a) -> m ()) -> m ()
ifIndirect (SIndirect i) f = f i
ifIndirect _ _ = pure ()

credit :: MonadInherit m => Stream m a -> m ()
credit s = ifIndirect s (`creditWith` 1)

evalone :: MonadInherit m => Stream m a -> m ()
evalone s = ifIndirect s (void . force)

sappend :: MonadInherit m => Stream m a -> Stream m a -> m (Stream m a)
sappend xs ys = credit ys >> evalone ys >> smatch xs
  (\x xs -> SCons x <$> taildelay (SAppend xs ys))
  (pure ys)

walk s = smatch s (\_ xs -> walk xs) (pure ())

foo :: MonadInherit m => Stream m a -> m ()
foo s = smatch s (\_ _ -> pure ()) (pure ())

test :: MonadInherit m => m ()
test = do
  s <- sappend (SCons 1 SNil) (SCons 2 SNil)
  credit s >> credit s
  foo s
  credit s
  walk s

toList :: MonadLazy m => Stream m a -> m [a]
toList SNil = pure []
toList (SCons x xs) = do
  xs' <- toList xs
  pure $ x : xs'
toList (SIndirect t) = do
  lazymatch t toList $ \case
      SAppend xs ys -> do
        xs' <- toList xs
        ys' <- toList ys
        pure $ xs' ++ ys'
      SReverse xs ys -> do
        xs' <- toList xs
        ys' <- toList ys
        pure $ reverse xs' ++ ys'

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (SLazyCon m a) where
  prettyCell (SAppend xs ys) = do
    xs' <- prettyCell xs
    ys' <- prettyCell ys
    pure $ mkMCell "SAppend" [xs', ys']
  prettyCell (SReverse xs ys) = do
    xs' <- prettyCell xs
    ys' <- prettyCell ys
    pure $ mkMCell "SReverse" [xs', ys']

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (Stream m a) where
  prettyCell xs = mkMList <$> toList xs <*> toHole xs
    where
      toList SNil = pure $ []
      toList (SCons x xs) = (:) <$> prettyCell x <*> toList xs
      toList (SIndirect t) = pure $ []

      toHole SNil = pure $ Nothing
      toHole (SCons x xs) = toHole xs
      toHole (SIndirect t) = Just <$> prettyCell t