packages feed

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

{-# LANGUAGE GADTs, LambdaCase #-}

module Test.Credit.Queue.Streams (Stream, StreamCell(..), SLazyCon(..), cons, nil, slength, toList, test) where

import Control.Monad
import Control.Monad.Credit

type Stream m a = Thunk m (SLazyCon m) (StreamCell m a)

data StreamCell m a
  = SCons a (Stream m a)
  | SNil

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

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

cons :: MonadLazy m => a -> Stream m a -> m (Stream m a)
cons x xs = value $ SCons x xs

nil :: MonadLazy m => m (Stream m a)
nil = value SNil

sreverse :: MonadInherit m
          => Stream m a -> Stream m a -> m (Stream m a)
sreverse xs ys = tick >> force xs >>= \case
  SCons x xs -> sreverse xs =<< cons x ys
  SNil -> pure ys

sappend :: MonadInherit m
         => Stream m a -> Stream m a -> m (Stream m a)
sappend xs ys = do
  tick
  ys `creditWith` 1
  force xs >>= \case
    SCons x xs' -> do
      xs'ys <- delay $ SAppend xs' ys
      creditAllTo xs'ys
      cons x xs'ys
    SNil -> creditAllTo ys >> pure ys

cellToList :: MonadLazy m => StreamCell m a -> m [a]
cellToList SNil = pure []
cellToList (SCons x xs) = (x :) <$> toList xs

toList :: MonadLazy m => Stream m a -> m [a]
toList t = do
  lazymatch t cellToList $ \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'

slength :: MonadLazy m => Stream m a -> m Int
slength s = length <$> toList s

walk :: MonadInherit m => Stream m a -> m ()
walk s = force s >>= \case
  SCons _ xs -> walk xs
  SNil -> pure ()

foo :: MonadInherit m => Stream m a -> m ()
foo s = void $ force s

test :: MonadInherit m => m ()
test = do
  nil <- value SNil
  one <- value (SCons 1 nil)
  two <- value (SCons 2 nil)
  s <- sappend one two
  creditWith s 2
  foo s
  creditWith s 1
  walk s

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 (StreamCell m a) where
  prettyCell xs = mkMList <$> toList xs <*> toHole xs
    where
      toList SNil = pure $ []
      toList (SCons x xs) = (:) <$> prettyCell x <*> lazymatch xs toList (\_ -> pure [])

      toHole SNil = pure $ Nothing
      toHole (SCons x xs) = lazymatch xs toHole (\_ -> Just <$> prettyCell xs)