streaming-with (empty) → 0.1.0.0
raw patch · 7 files changed
+379/−0 lines, 7 filesdep +basedep +exceptionsdep +managedsetup-changed
Dependencies added: base, exceptions, managed, streaming-bytestring, transformers
Files
- ChangeLog.md +5/−0
- LICENSE +20/−0
- README.md +110/−0
- Setup.hs +2/−0
- src/Streaming/With.hs +74/−0
- src/Streaming/With/Lifted.hs +137/−0
- streaming-with.cabal +31/−0
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for streaming-with++## 0.1.0.0 -- 2017-06-30++* First release.
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2017 Ivan Lazar Miljenovic++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,110 @@+streaming-with+==============++[](https://hackage.haskell.org/package/streaming-with) [](https://travis-ci.org/ivan-m/streaming-with)++> with/bracket-style idioms for use with [streaming]++This library provides resource management for the [streaming]+ecosystem of libraries using bracketed continuations.++[streaming]: http://hackage.haskell.org/package/streaming++Currently, these only contain file-handling utilities; if you can+think of any more functions that fit in here please let me know!++There are two ways of using this library:++1. Explicitly pass around the continuations using `Streaming.With`.++2. If you have a lot of nested continuations, you may prefer using+ `Streaming.With.Lifted` with either [`ContT`] or [managed]; these+ will allow you to pass around the parameters to the continuations.++[`ContT`]: http://hackage.haskell.org/packages/archive/transformers/latest/doc/html/Control-Monad-Trans-Cont.html#v:ContT+[managed]: http://hackage.haskell.org/package/managed++Motivation+----------++The streaming library has some usages of `MonadResource` from the+[resourcet] package to try and perform resource allocation; in+comparison to `conduit` however, streaming doesn't support prompt+finalisation. Furthermore, because in may ways `Conduit`s can be+considered to be a datatype representing monadic functions between two+types whereas a single `Stream` is more akin to a producing-only+`Conduit` or `Pipe`, attempts at having something like `readFile` that+returns a `Stream` does not always lead to the resource being closed+at the correct point. The consensus in the issue for [promptness for+streaming] was that perhaps relying upon `MonadResource` was not the+correct approach.++[resourcet]: http://hackage.haskell.org/package/resourcet+[promptness for streaming]: https://github.com/michaelt/streaming/issues/23+++The [Bracket pattern] (also known as the _with..._ idiom, or a subset+of continuation passing style) allows for a convenient way to obtain+and then guarantee the release of (possibly scarce) resources. This+can be further enhanced when dealing with nested usages of these with+the use of either the [`ContT`] managed transformer or the [managed]+library (where `Managed` -- or at least its safe variant -- is+isomorphic to `ContT () IO`).++[Bracket pattern]: https://wiki.haskell.org/Bracket_pattern++The biggest downside of using `bracket` from the standard `base`+library is that the types are not very convenient in the world of+monad transformers: it is limited to `IO` computations only, which+means for our use of streaming that we could only use `Stream f IO r`+without any state, logging, etc. However, the [exceptions] library+contains a more general purpose variant that provides us with extra+flexibility; it doesn't even need to be in `IO` if you have a+completely pure `Stream`! (A variant is also available in+[lifted-base], but it is less used and streaming already depends upon+exceptions).++[exceptions]: http://hackage.haskell.org/package/exceptions+[lifted-base]: http://hackage.haskell.org/package/lifted-base++Disadvantages+-------------++Whilst the bracket pattern is powerful, it does have some downsides of+which you should be aware (specifically compared to [resourcet] which+is the main alternative).++First of all, independent of its usage with streaming, is that whilst+`bracket` predates [resourcet], the latter is [more powerful].+Whether this extra power is of use to you is up to you, but it does+mean that you in effect have lots of nested resource management rather+than just one overall resource control.++[more powerful]: http://www.yesodweb.com/blog/2013/03/resourcet-overview++The obvious disadvantage of using the bracket pattern is that it does+not fit in as nicely in the function composition style that usage of+streaming enables compared to other stream processing libraries. This+can be mitigated somewhat with using the lifted variants in this+package which allows you to operate monadically (which still isn't as+nice but may be preferable to lots of explicitly nested continuations).++Furthermore, without prompt finalisation the same "long running+computation" [issue] is relevant. For example, consider something+that looks like this:++```haskell+withBinaryFile "myFile" ReadMode $+ doSomethingWithEachLine+ . B.lines+ . B.hGetContents+```++Ideally, after the last chunk from the file is read, the file handle+would be closed. However, that is not the case: it's not until the+_entire computation_ is complete that the handle is closed. Note,+however, that the same limitation is present when using+`MonadResource`: this is a limitation of the `Stream` type, not on how+we choose to allocate and manage resources.++[issue]: http://www.yesodweb.com/blog/2013/10/core-flaw-pipes-conduit
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Streaming/With.hs view
@@ -0,0 +1,74 @@+{- |+ Module : Streaming.With+ Description : with/bracket-style idioms for use with streaming+ Copyright : Ivan Lazar Miljenovic+ License : MIT+ Maintainer : Ivan.Miljenovic@gmail.com++++ -}+module Streaming.With+ ( -- * File-handling+ withFile+ , withBinaryFile+ -- ** Common file-handling cases+ , writeBinaryFile+ , appendBinaryFile+ , withBinaryFileContents+ -- ** Re-exports+ -- $reexports+ , MonadMask+ , bracket+ ) where++import Data.ByteString.Streaming (ByteString)+import qualified Data.ByteString.Streaming as B++import Control.Monad.Catch (MonadMask, bracket)+import Control.Monad.IO.Class (MonadIO, liftIO)+import System.IO (Handle, IOMode(..), hClose, openBinaryFile,+ openFile)++--------------------------------------------------------------------------------++-- | A lifted variant of 'System.IO.withFile'.+--+-- You almost definitely don't want to use this; instead, use+-- 'withBinaryFile' in conjunction with "Data.ByteString.Streaming".+withFile :: (MonadMask m, MonadIO m) => FilePath -> IOMode -> (Handle -> m r) -> m r+withFile fp md = bracket (liftIO (openFile fp md)) (liftIO . hClose)++-- | A lifted variant of 'System.IO.withBinaryFile'.+withBinaryFile :: (MonadMask m, MonadIO m) => FilePath -> IOMode -> (Handle -> m r) -> m r+withBinaryFile fp md = bracket (liftIO (openBinaryFile fp md)) (liftIO . hClose)++-- | Write to the specified file.+writeBinaryFile :: (MonadMask m, MonadIO m) => FilePath -> ByteString m r -> m r+writeBinaryFile fp = withBinaryFile fp WriteMode . flip B.hPut++-- | Append to the specified file.+appendBinaryFile :: (MonadMask m, MonadIO m) => FilePath -> ByteString m r -> m r+appendBinaryFile fp = withBinaryFile fp AppendMode . flip B.hPut++-- | Apply a function to the contents of the file.+--+-- Note that a different monadic stack is allowed for the+-- 'ByteString' input, as long as it later gets resolved to the+-- required output type (e.g. remove transformer).+withBinaryFileContents :: (MonadMask m, MonadIO m, MonadIO n) => FilePath+ -> (ByteString n () -> m r) -> m r+withBinaryFileContents fp f = withBinaryFile fp ReadMode (f . B.hGetContents)++--------------------------------------------------------------------------------++{- $reexports++These may assist in writing your own bracket-style functions.++Note that not everything is re-exported: for example, 'Handle' isn't+re-exported for use with 'withFile' as it's unlikely that you will+write another wrapper around it, and furthermore it wouldn't be a+common enough extension to warrant it.++-}
+ src/Streaming/With/Lifted.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE FlexibleContexts, RankNTypes, TypeFamilies #-}++{- |+ Module : Streaming.With.Lifted+ Description : Lifted with/bracket-style idioms for use with streaming+ Copyright : Ivan Lazar Miljenovic+ License : MIT+ Maintainer : Ivan.Miljenovic@gmail.com++ Both the 'ContT' and 'Managed' (which is a specialised variant of+ 'ContT') monads can help with writing heavily nested bracketed+ code, by being able to pass around the argument to each continuation.++ This module - through the use of the 'Withable' class - provides+ lifted variants of "Streaming.With" to be able to automatically use+ these functions for resource management in your choice of monad.++ Note that you still need to use the specific monad's running+ function, as it is not possible to encapsulate those in a generic+ fashion (unless we wanted to constrain the 'ContT' instance to+ @ContT ()@).++ To ensure resources don't leak out, it is preferred that if using+ 'ContT', you keep the final result type to @()@ (which is what+ 'Managed' recommends with its 'runManaged' function).++ As an example using 'Managed', this function will copy the contents+ of two files into a third.:++ > copyBoth :: FilePath -> FilePath -> FilePath -> IO ()+ > copyBoth inF1 inF2 outF = runManaged $ do+ > bs1 <- withBinaryFileContents inF1+ > bs2 <- withBinaryFileContents inF2+ > writeBinaryFile outF bs1+ > appendBinaryFile outF bs2++ -}+module Streaming.With.Lifted+ ( Withable (..)+ -- * File-handling+ , withFile+ , withBinaryFile+ -- ** Common file-handling cases+ , writeBinaryFile+ , appendBinaryFile+ , withBinaryFileContents+ -- ** Re-exports+ -- $reexports+ , MonadMask+ , bracket+ ) where++import Data.ByteString.Streaming (ByteString)+import qualified Streaming.With as W++import Control.Monad.Catch (MonadMask, bracket)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Managed (Managed, managed)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Cont (ContT(..))+import System.IO (Handle, IOMode)++--------------------------------------------------------------------------------++-- | How to automatically lift bracket-style expressions into a monad.+--+-- The constraints are common ones found throughout this module, and+-- as such incorporated into this class to avoid repetition in all+-- the type signatures.+--+-- It is highly recommended that you do /not/ try and layer extra+-- transformers on top of this; the intent of this class is just to+-- make writing all the underlying continuations in a nicer fashion+-- without explicit nesting, rather than as the basis of lower-level+-- code.+class (MonadMask (WithMonad w), MonadIO (WithMonad w)) => Withable w where+ type WithMonad w :: * -> *++ liftWith :: (forall r. (a -> WithMonad w r) -> WithMonad w r) -> w a++ liftAction :: WithMonad w a -> w a++instance Withable Managed where+ type WithMonad Managed = IO++ liftWith = managed++ liftAction = liftIO++instance (MonadMask m, MonadIO m) => Withable (ContT r m) where+ type WithMonad (ContT r m) = m++ liftWith = ContT++ liftAction = lift++--------------------------------------------------------------------------------++-- | A lifted variant of 'System.IO.withFile'.+--+-- You almost definitely don't want to use this; instead, use+-- 'withBinaryFile' in conjunction with "Data.ByteString.Streaming".+withFile :: (Withable w) => FilePath -> IOMode -> w Handle+withFile fp md = liftWith (W.withFile fp md)++-- | A lifted variant of 'System.IO.withBinaryFile'.+withBinaryFile :: (Withable w) => FilePath -> IOMode -> w Handle+withBinaryFile fp md = liftWith (W.withBinaryFile fp md)++-- | Write to the specified file.+writeBinaryFile :: (Withable w) => FilePath -> ByteString (WithMonad w) r -> w r+writeBinaryFile fp inp = liftAction (W.writeBinaryFile fp inp)++-- | Append to the specified file.+appendBinaryFile :: (Withable w) => FilePath -> ByteString (WithMonad w) r -> w r+appendBinaryFile fp inp = liftAction (W.appendBinaryFile fp inp)++-- | Apply a function to the contents of the file.+--+-- Note that a different monadic stack is allowed for the+-- 'ByteString' input, as long as it later gets resolved to the+-- required output type (e.g. remove transformer).+withBinaryFileContents :: (Withable w, MonadIO n) => FilePath -> w (ByteString n ())+withBinaryFileContents fp = liftWith (W.withBinaryFileContents fp)++--------------------------------------------------------------------------------++{- $reexports++These may assist in writing your own bracket-style functions.++Note that not everything is re-exported: for example, 'Handle' isn't+re-exported for use with 'withFile' as it's unlikely that you will+write another wrapper around it, and furthermore it wouldn't be a+common enough extension to warrant it.++-}
+ streaming-with.cabal view
@@ -0,0 +1,31 @@+name: streaming-with+version: 0.1.0.0+synopsis: with/bracket-style idioms for use with streaming+description:+ This package provides the foundations for a continuation-based+ approach for dealing with resources in the streaming ecosystem.+license: MIT+license-file: LICENSE+author: Ivan Lazar Miljenovic+maintainer: Ivan.Miljenovic@gmail.com+copyright: Ivan Lazar Miljenovic+category: Data, Streaming+build-type: Simple+extra-source-files: ChangeLog.md, README.md+cabal-version: >=1.10+tested-with: GHC == 7.10.2, GHC == 8.0.2, GHC == 8.1.*++source-repository head+ type: git+ location: https://github.com/ivan-m/streaming-with.git++library+ exposed-modules: Streaming.With+ , Streaming.With.Lifted+ build-depends: base == 4.*+ , exceptions >= 0.6 && < 0.9+ , managed == 1.0.*+ , streaming-bytestring >= 0.1.0.3 && < 0.2+ , transformers+ hs-source-dirs: src+ default-language: Haskell2010