Skip to content

Commit

Permalink
Move the Stream type and related definitions into a separate module.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorisdral committed May 30, 2023
1 parent 16b0762 commit e2d09d0
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 71 deletions.
1 change: 1 addition & 0 deletions fs-sim/fs-sim.cabal
Expand Up @@ -34,6 +34,7 @@ library
System.FS.Sim.MockFS
System.FS.Sim.Pure
System.FS.Sim.STM
System.FS.Sim.Stream

default-language: Haskell2010
build-depends:
Expand Down
82 changes: 11 additions & 71 deletions fs-sim/src/System/FS/Sim/Error.hs
@@ -1,11 +1,9 @@
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE UndecidableInstances #-}

-- | 'HasFS' instance wrapping 'SimFS' that generates errors, suitable for
-- testing error handling.
Expand All @@ -19,12 +17,6 @@ module System.FS.Sim.Error (
, ErrorStream
, ErrorStreamGetSome
, ErrorStreamPutSome
, Stream (..)
, always
, mkStream
, mkStreamGen
, null
, runStream
-- * Generating partial reads/writes
, Partial (..)
, hGetSomePartial
Expand All @@ -46,7 +38,7 @@ module System.FS.Sim.Error (
import Prelude hiding (null)

import Control.Concurrent.Class.MonadSTM.Strict
import Control.Monad (replicateM, void)
import Control.Monad (void)
import Control.Monad.Class.MonadThrow hiding (handle)

import Data.ByteString (ByteString)
Expand All @@ -55,9 +47,9 @@ import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as LC8
import Data.Foldable (for_)
import Data.List (dropWhileEnd, intercalate)
import Data.List (intercalate)
import qualified Data.List as List
import Data.Maybe (catMaybes, isNothing)
import Data.Maybe (catMaybes)
import Data.String (IsString (..))
import Data.Word (Word64)

Expand All @@ -72,64 +64,12 @@ import System.FS.API.Types

import System.FS.Sim.MockFS (HandleMock, MockFS)
import qualified System.FS.Sim.STM as Sim
import System.FS.Sim.Stream

{-------------------------------------------------------------------------------
Streams
Streams of errors
-------------------------------------------------------------------------------}

-- | A 'Stream' is a possibly infinite stream of @'Maybe' a@s.
newtype Stream a = Stream { getStream :: [Maybe a] }
deriving (Show, Functor)

instance Semigroup (Stream a) where
Stream s1 <> Stream s2 = Stream (zipWith pickLast s1 s2)
where
pickLast (Just x) Nothing = Just x
pickLast _ mbY = mbY

instance Monoid (Stream a) where
mempty = Stream (repeat Nothing)
mappend = (<>)

-- | Create a 'Stream' based on the given possibly infinite list of @'Maybe'
-- a@s.
mkStream :: [Maybe a] -> Stream a
mkStream = Stream

-- | Advance the 'Stream'. Return the @'Maybe' a@ and the remaining 'Stream'.
runStream :: Stream a -> (Maybe a, Stream a)
runStream s@(Stream []) = (Nothing, s)
runStream (Stream (a:as)) = (a, Stream as)

-- | Make a 'Stream' that always generates the given @a@.
always :: a -> Stream a
always a = Stream (repeat (Just a))

-- | Make a 'Stream' generator based on a @a@ generator.
--
-- The generator generates a finite stream of 10 elements, where each element
-- has a chance of being either 'Nothing' or an element generated with the
-- given @a@ generator (wrapped in a 'Just').
--
-- The first argument is the likelihood (as used by 'QC.frequency') of a
-- 'Just' where 'Nothing' has likelihood 2.
mkStreamGen :: Int -> Gen a -> Gen (Stream a)
mkStreamGen justLikelihood genA =
mkStream . dropWhileEnd isNothing <$> replicateM 10 mbGenA
where
mbGenA = QC.frequency
[ (2, return Nothing)
, (justLikelihood, Just <$> genA)
]

-- | Return 'True' if the stream is empty.
--
-- A stream consisting of only 'Nothing's (even if it is only one) is not
-- considered to be empty.
null :: Stream a -> Bool
null (Stream []) = True
null _ = False

-- | An 'ErrorStream' is a possibly infinite 'Stream' of (@Maybe@)
-- @'FsErrorType'@s.
--
Expand Down
74 changes: 74 additions & 0 deletions fs-sim/src/System/FS/Sim/Stream.hs
@@ -0,0 +1,74 @@
{-# LANGUAGE DeriveFunctor #-}

module System.FS.Sim.Stream (
Stream (..)
, always
, mkStream
, mkStreamGen
, null
, runStream
) where

import Control.Monad (replicateM)
import Data.List (dropWhileEnd)
import Data.Maybe (isNothing)
import Prelude hiding (null)
import qualified Test.QuickCheck as QC
import Test.QuickCheck (Gen)

{-------------------------------------------------------------------------------
Streams
-------------------------------------------------------------------------------}

-- | A 'Stream' is a possibly infinite stream of @'Maybe' a@s.
newtype Stream a = Stream { getStream :: [Maybe a] }
deriving (Show, Functor)

instance Semigroup (Stream a) where
Stream s1 <> Stream s2 = Stream (zipWith pickLast s1 s2)
where
pickLast (Just x) Nothing = Just x
pickLast _ mbY = mbY

instance Monoid (Stream a) where
mempty = Stream (repeat Nothing)
mappend = (<>)

-- | Create a 'Stream' based on the given possibly infinite list of @'Maybe'
-- a@s.
mkStream :: [Maybe a] -> Stream a
mkStream = Stream

-- | Advance the 'Stream'. Return the @'Maybe' a@ and the remaining 'Stream'.
runStream :: Stream a -> (Maybe a, Stream a)
runStream s@(Stream []) = (Nothing, s)
runStream (Stream (a:as)) = (a, Stream as)

-- | Make a 'Stream' that always generates the given @a@.
always :: a -> Stream a
always a = Stream (repeat (Just a))

-- | Make a 'Stream' generator based on a @a@ generator.
--
-- The generator generates a finite stream of 10 elements, where each element
-- has a chance of being either 'Nothing' or an element generated with the
-- given @a@ generator (wrapped in a 'Just').
--
-- The first argument is the likelihood (as used by 'QC.frequency') of a
-- 'Just' where 'Nothing' has likelihood 2.
mkStreamGen :: Int -> Gen a -> Gen (Stream a)
mkStreamGen justLikelihood genA =
mkStream . dropWhileEnd isNothing <$> replicateM 10 mbGenA
where
mbGenA = QC.frequency
[ (2, return Nothing)
, (justLikelihood, Just <$> genA)
]

-- | Return 'True' if the stream is empty.
--
-- A stream consisting of only 'Nothing's (even if it is only one) is not
-- considered to be empty.
null :: Stream a -> Bool
null (Stream []) = True
null _ = False

0 comments on commit e2d09d0

Please sign in to comment.