managed (empty) → 1.0.0
raw patch · 5 files changed
+277/−0 lines, 5 filesdep +basedep +transformerssetup-changed
Dependencies added: base, transformers
Files
- LICENSE +24/−0
- Setup.hs +2/−0
- managed.cabal +40/−0
- src/Control/Monad/Managed.hs +178/−0
- src/Control/Monad/Managed/Safe.hs +33/−0
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2014 Gabriel Gonzalez+All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright notice,+ this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.+ * Neither the name of Gabriel Gonzalez nor the names of other contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ managed.cabal view
@@ -0,0 +1,40 @@+Name: managed+Version: 1.0.0+Cabal-Version: >=1.8.0.2+Build-Type: Simple+License: BSD3+License-File: LICENSE+Copyright: 2014 Gabriel Gonzalez+Author: Gabriel Gonzalez+Maintainer: Gabriel439@gmail.com+Bug-Reports: https://github.com/Gabriel439/Haskell-Managed-Library/issues+Synopsis: A monad for managed values+Description: In Haskell you very often acquire values using the @with...@+ idiom using functions of type @(a -> IO r) -> IO r@. This idiom forms a+ @Monad@, which is a special case of the @ContT@ monad (from @transformers@) or+ the @Codensity@ monad (from @kan-extensions@). The main purpose behind this+ package is to provide a restricted form of these monads specialized to this+ unusually common case.+ .+ The reason this package defines a specialized version of these types is to:+ .+ * be more beginner-friendly,+ .+ * simplify inferred types and error messages, and:+ .+ * provide some additional type class instances that would otherwise be orphan+ instances+Category: Control+Source-Repository head+ Type: git+ Location: https://github.com/Gabriel439/Haskell-Managed-Library++Library+ Hs-Source-Dirs: src+ Build-Depends:+ base >= 4 && < 5 ,+ transformers >= 0.2.0.0 && < 0.5 + Exposed-Modules:+ Control.Monad.Managed,+ Control.Monad.Managed.Safe+ GHC-Options: -O2 -Wall
+ src/Control/Monad/Managed.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE RankNTypes #-}++{-| An example Haskell program to copy data from one handle to another might+ look like this:++> main =+> withFile "inFile.txt" ReadMode $ \inHandle ->+> withFile "outFile.txt" WriteMode $ \outHandle ->+> copy inHandle outHandle+>+> -- A hypothetical function that copies data from one handle to another+> copy :: Handle -> Handle -> IO ()++ `System.IO.withFile` is one of many functions that acquire some resource in+ an exception-safe way. These functions take a callback function as an+ argument and they invoke the callback on the resource when it becomes+ available, guaranteeing that the resource is properly disposed if the+ callback throws an exception.++ These functions usually have a type that ends with the following pattern:++> Callback+> -- -----------+> withXXX :: ... -> (a -> IO r) -> IO r++ Here are some examples of this pattern from the @base@ libraries:++> withArray :: Storable a => [a] -> (Ptr a -> IO r) -> IO r+> withBuffer :: Buffer e -> (Ptr e -> IO r) -> IO r+> withCAString :: String -> (CString -> IO r) -> IO r+> withForeignPtr :: ForeignPtr a -> (Ptr a -> IO r) -> IO r+> withMVar :: Mvar a -> (a -> IO r) -> IO r+> withPool :: (Pool -> IO r) -> IO r++ Acquiring multiple resources in this way requires nesting callbacks.+ However, you can wrap anything of the form @((a -> IO r) -> IO r)@ in the+ `Managed` monad, which translates binds to callbacks for you:++> import Control.Monad.Managed+> import System.IO+>+> inFile :: FilePath -> Managed Handle+> inFile filePath = managed (withFile filePath ReadMode)+>+> outFile :: FilePath -> Managed Handle+> outFile filePath = managed (withFile filePath WriteMode)+>+> main = runManaged $ do+> inHandle <- inFile "inFile.txt"+> outHandle <- outFile "outFile.txt"+> liftIO (copy inHandle outHandle)++ ... or you can just wrap things inline:++> main = runManaged $ do+> inHandle <- managed (withFile "inFile.txt" ReadMode)+> outHandle <- managed (withFile "outFile.txt" WriteMode)+> liftIO (copy inHandle outHandle)++ Additionally, since `Managed` is a `Monad`, you can take advantage of all+ your favorite combinators from "Control.Monad". For example, the+ `Foreign.Marshal.Utils.withMany` function from "Foreign.Marshal.Utils"+ becomes a trivial wrapper around `mapM`:++> withMany :: (a -> (b -> IO r) -> IOr ) -> [a] -> ([b] -> IO r) -> IO r+> withMany f = with . mapM (Managed . f)++ Another reason to use `Managed` is that if you wrap a `Monoid` value in+ `Managed` you get back a new `Monoid`:++> instance Monoid a => Monoid (Managed a)++ This lets you combine managed resources transparently. You can also lift+ operations from some numeric type classes this way, too, such as the `Num`+ type class.+-}++module Control.Monad.Managed (+ -- * Managed+ Managed,+ managed,+ with,+ runManaged,++ -- * Re-exports+ -- $reexports+ module Control.Monad.IO.Class+ ) where++import Control.Applicative (Applicative(pure, (<*>)), liftA2)+import Control.Monad.IO.Class (MonadIO(liftIO))+import Data.Monoid (Monoid(mempty, mappend))++-- | A managed resource that you acquire using `with`+newtype Managed a = Managed { (>>-) :: forall r . (a -> IO r) -> IO r }++instance Functor Managed where+ fmap f mx = Managed (\return_ ->+ mx >>- \x ->+ return_ (f x) )++instance Applicative Managed where+ pure r = Managed (\return_ ->+ return_ r )++ mf <*> mx = Managed (\return_ ->+ mf >>- \f ->+ mx >>- \x ->+ return_ (f x) )++instance Monad Managed where+ return r = Managed (\return_ ->+ return_ r )++ ma >>= f = Managed (\return_ ->+ ma >>- \a ->+ f a >>- \b ->+ return_ b )++instance MonadIO Managed where+ liftIO m = Managed (\return_ -> do+ a <- m+ return_ a )++instance Monoid a => Monoid (Managed a) where+ mempty = pure mempty++ mappend = liftA2 mappend++instance Num a => Num (Managed a) where+ fromInteger = pure . fromInteger+ negate = fmap negate+ abs = fmap abs+ signum = fmap signum+ (+) = liftA2 (+)+ (*) = liftA2 (*)+ (-) = liftA2 (-)++instance Fractional a => Fractional (Managed a) where+ fromRational = pure . fromRational+ recip = fmap recip+ (/) = liftA2 (/)++instance Floating a => Floating (Managed a) where+ pi = pure pi+ exp = fmap exp+ sqrt = fmap sqrt+ log = fmap log+ sin = fmap sin+ tan = fmap tan+ cos = fmap cos+ asin = fmap sin+ atan = fmap atan+ acos = fmap acos+ sinh = fmap sinh+ tanh = fmap tanh+ cosh = fmap cosh+ asinh = fmap asinh+ atanh = fmap atanh+ acosh = fmap acosh+ (**) = liftA2 (**)+ logBase = liftA2 logBase++-- | Build a `Managed` value+managed :: (forall r . (a -> IO r) -> IO r) -> Managed a+managed = Managed++-- | Acquire a `Managed` value+with :: Managed a -> (a -> IO r) -> IO r+with = (>>-)++-- | Run a `Managed` computation, enforcing that no acquired resources leak+runManaged :: Managed () -> IO ()+runManaged m = m >>- return++{- $reexports+ "Control.Monad.IO.Class" re-exports 'MonadIO'+-}
+ src/Control/Monad/Managed/Safe.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE RankNTypes #-}++{-| This module is a safer subset of "Control.Monad.Managed" that only lets you+ unwrap the `Managed` type using `runManaged`. This enforces that you never+ leak acquired resources from a `Managed` computation.++ In general, you should strive to propagate the `Managed` type as much as+ possible and use `runManaged` when you are done with acquired resources.+ However, there are legitimate circumstances where you want to return a value+ other than acquired resource from the bracketed computation, which requires+ using `Control.Monad.Managed.with`.++ This module is not the default because you can also use the `Managed` type+ for callback-based code that is completely unrelated to resources.+-}++module Control.Monad.Managed.Safe (+ -- * Managed+ Managed,+ managed,+ runManaged,++ -- * Re-exports+ -- $reexports+ module Control.Monad.IO.Class+ ) where++import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.Monad.Managed (Managed, managed, runManaged)++{- $reexports+ "Control.Monad.IO.Class" re-exports 'MonadIO'+-}