streaming-bracketed 0.1.0.0 → 0.1.0.1
raw patch · 6 files changed
+101/−31 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- README.md +8/−8
- library/Streaming/Bracketed.hs +9/−8
- library/Streaming/Bracketed/Internal.hs +66/−11
- streaming-bracketed.cabal +3/−3
- tests/doctests.hs +1/−1
- tests/tests.hs +14/−0
README.md view
@@ -46,10 +46,10 @@ [CHANGELOG](http://hackage.haskell.org/package/streaming-0.2.1.0/changelog) of the [streaming](http://hackage.haskell.org/package/streaming) package: -> Remove bracketStream, MonadCatch instance, and everything dealing with-> ResourceT. All of these things of sort of broken for Stream since there is no-> guarantee of linear consumption (functions like take can prevent finalizers-> from running).+ Remove bracketStream, MonadCatch instance, and everything dealing with+ ResourceT. All of these things of sort of broken for Stream since there is no+ guarantee of linear consumption (functions like take can prevent finalizers+ from running). [One Github issue](https://github.com/haskell-streaming/streaming/issues/52). @@ -57,9 +57,9 @@ [Streaming libs exercise/challenge](https://twitter.com/DiazCarrete/status/1016073374458671104): -> Given a list [(Filepath,Int,Int)] of files and line ranges, create a stream-> of lines belonging to the concatenated ranges.+ Given a list [(Filepath,Int,Int)] of files and line ranges, create a stream+ of lines belonging to the concatenated ranges. -> Prompt release of file handles is required. resource-handling monads and-> "withXXX"-style functions are allowed.+ Prompt release of file handles is required. resource-handling monads and+ "withXXX"-style functions are allowed.
library/Streaming/Bracketed.hs view
@@ -1,8 +1,8 @@ {-| A resource management decorator for `Stream`s. - Resource-allocating 'Stream's are lifted to values of type `Bracketed` that- can be combined as such, using an API that is more restricted than- that of the original 'Stream's and ensures prompt deallocation of+ 'Stream's requiring resource allocation are lifted to values of type+ `Bracketed`, which can be combined using an API that is more restricted+ than that of the original 'Stream's and ensures prompt deallocation of resources. Values of type `Bracketed` can later be run by supplying a@@ -24,19 +24,19 @@ -} module Streaming.Bracketed (- -- * Bracketed+ -- * Bracketed Bracketed- -- * Lifting streams+ -- * Lifting streams , clear , bracketed- -- * Consuming bracketed streams with continuations+ -- * Consuming bracketed streams with continuations , with , with_- -- * Transforming bracketed streams+ -- * Transforming bracketed streams , over , over_ , for - -- * Reading text files+ -- * Reading text files , linesFromFile , concatRanges ) where@@ -56,3 +56,4 @@ >>> import qualified Streaming.Bracketed as R -}+
library/Streaming/Bracketed/Internal.hs view
@@ -12,6 +12,25 @@ import System.IO +{- $setup++>>> import Data.Foldable+>>> import Control.Monad+>>> import System.IO+>>> import System.FilePath+>>> import System.Directory+>>> import Streaming+>>> import qualified Streaming.Prelude as S+>>> import qualified Streaming.Bracketed as R++-}++-- | A resource management decorator for the `Stream` type.+-- +-- @a@ is the type of yielded elements, @r@ the type of the final result.+--+-- It is not parameterized by a base monad because the underlying+-- `Stream`s are always over `IO`. newtype Bracketed a r = Bracketed { runBracketed :: IORef Finstack -> Stream (Of a) IO r } deriving Functor@@ -21,6 +40,7 @@ first f (Bracketed b) = Bracketed (S.map f . b) second = fmap +-- | `*>` performs sequential composition. instance Applicative (Bracketed a) where pure = Bracketed . const . pure Bracketed b <*> Bracketed b' = Bracketed (\finref ->@@ -41,11 +61,26 @@ -- Finalizers at the head of the list correspond to deeper levels of nesting. data Finstack = Finstack !Int [IO ()] --- | Lift a `Stream` that doesn't perform allocation to a `Bracketed`.+{-| Lift a `Stream` that doesn't perform allocation to a `Bracketed`.+ +>>> R.with (R.clear (S.yield True)) S.toList+[True] :> ()+ +-} clear :: Stream (Of x) IO r -> Bracketed x r clear stream = Bracketed (const stream) --- | Lift a `Stream` that requires resource allocation to a `Bracketed`.+{-| Lift a `Stream` that performs resource allocation to a `Bracketed`.+ + The first argument allocates the resource, the second is a function+ that deallocates it.+ +>>> R.with (R.bracketed (putStrLn "alloc") (\() -> putStrLn "dealloc") (\() -> S.yield True)) S.toList+alloc+dealloc+[True] :> ()++ -} bracketed :: IO a -> (a -> IO ()) -> (a -> Stream (Of x) IO r) -> Bracketed x r bracketed allocate finalize stream = Bracketed (\finref -> let open = do @@ -58,30 +93,50 @@ liftIO (mask (\_ -> reset size0 finref)) pure r) --- | Consume a `Bracketed` stream, exhausting it.+{-| Consume a `Bracketed` stream, exhausting it.+ +>>> R.with (pure True) S.toList+[] :> True+ +-} with :: Bracketed a r -> (forall x. Stream (Of a) IO x -> IO (Of b x)) -> IO (Of b r) with (Bracketed b) f = Control.Exception.bracket (newIORef (Finstack 0 [])) (reset 0) (f . b) --- | Consume a `Bracketed` stream, possibly wihout exhausting it.--- --- Finalizers lying in unconsumed parts of the stream will not be executed--- until the callback returns, so better not tarry too long if you want--- prompt finalization.+{-| Consume a `Bracketed` stream, possibly wihout exhausting it.+ + Finalizers lying in unconsumed parts of the stream will not be executed+ until the callback returns, so better not tarry too long if you want+ prompt finalization.++>>> R.with_ (R.clear (S.each "abcd" *> pure True)) (S.toList . S.take 2)+"ab" :> ()++-} with_ :: Bracketed a r -> (Stream (Of a) IO r -> IO b) -> IO b with_ (Bracketed b) f = Control.Exception.bracket (newIORef (Finstack 0 [])) (reset 0) (f . b) --- | Apply to the underlying stream a transformation that preserves the return value.+{-| Apply to the underlying stream a transformation that preserves the return value, like 'S.map'.++>>> R.with (S.map succ `R.over` R.clear (S.each "abcd")) S.toList+"bcde" :> ()++-} over :: (forall x. Stream (Of a) IO x -> Stream (Of b) IO x) -> Bracketed a r -> Bracketed b r over transform (Bracketed b) = Bracketed (transform . b) --- | Apply to the underlying stream a transformation that might not preserve--- the return value.+{-| Apply to the underlying stream a transformation that might not preserve+ the return value, like 'S.take'.++>>> R.with (S.take 2 `R.over_` R.clear (S.each "abdc")) S.toList+"ab" :> ()+ +-} over_ :: (Stream (Of a) IO r -> Stream (Of b) IO r') -> Bracketed a r -> Bracketed b r' over_ transform (Bracketed b) = Bracketed (\finref -> let level = do
streaming-bracketed.cabal view
@@ -1,13 +1,13 @@ name: streaming-bracketed-version: 0.1.0.0+version: 0.1.0.1 synopsis: A resource management decorator for "streaming". description: This package provides a decorator for the Stream type from- the "streaming" package, that lets you perform bracket-like + "streaming", that lets you perform bracket-like operations that allocate and deallocate resources used by the stream. . By carefully managing the operations that are lifted to- the bracketed streams, we can ensure that finalizers are+ the decorated streams, we can ensure that finalizers are promptly called even with operations like "take", which do not consume the whole stream. license: MIT
tests/doctests.hs view
@@ -4,4 +4,4 @@ main :: IO () main = doctest- ["-ilibrary","library/Streaming/Bracketed.hs"]+ ["-ilibrary","library/Streaming/Bracketed.hs","library/Streaming/Bracketed/Internal.hs"]
tests/tests.hs view
@@ -45,6 +45,7 @@ , testCase "for" testFor , testCase "forException" testForException , testCase "forCleanupException" testForCleanupException+ , testCase "forCleanupException2" testForCleanupException2 , testCase "forTake" testForTake , testGroup "file stuff"@@ -147,6 +148,19 @@ res <- reverse <$> readIORef ref assertEqual "stream results" "xuijvy" res +testForCleanupException2 :: Assertion+testForCleanupException2 = + do ref <- newIORef ""+ let b = R.bracketed (modifyIORef' ref ('x':)) + (\_ -> modifyIORef' ref ('y':))+ (\_ -> S.each "ab") + f _ = R.bracketed (modifyIORef' ref ('u':)) + (\_ -> fail "finoops")+ (\_ -> S.each "ij") + _ :: Either IOException (Of () ()) <- try (R.with (R.for b f) (\stream ->+ S.foldM (\() c -> modifyIORef' ref (c:)) (pure ()) pure stream))+ res <- reverse <$> readIORef ref+ assertEqual "stream results" "xuijy" res testForTake :: Assertion testForTake =