packages feed

IOR (empty) → 0.1

raw patch · 10 files changed

+506/−0 lines, 10 filesdep +basedep +mtlsetup-changed

Dependencies added: base, mtl

Files

+ COPYING view
@@ -0,0 +1,25 @@+Copyright (c) 2008, Ivan Tomac+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 the author nor the names of the 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.
+ IOR.cabal view
@@ -0,0 +1,25 @@+name:               IOR+version:            0.1+synopsis:           Region based resource management for the IO monad.+description:        IOR monad is a wrapper around IO that allows region based+                    resource management.+category:           System, Monadic Regions+cabal-version:   >= 1.2+license:            BSD3+license-file:       COPYING+author:             Ivan Tomac+maintainer:         tomac `at` pacific `dot` net `dot` au+stability:          experimental+build-type:         Simple+data-files:         README+extra-source-files: src/Example.hs+                    src/Test.hs++library+  build-depends:        base, mtl+  ghc-options:          -Wall+  hs-source-dirs:       src+  exposed-modules:      Data.IORRef+                        System.IOR+                        System.IOR.Resource+  other-modules:        System.IOR.Internal
+ README view
@@ -0,0 +1,24 @@+To build the library type++    runhaskell Setup.lhs configure+    runhaskell Setup.lhs build+    runhaskell Setup.lhs haddock    # optional+    runhaskell Setup.lhs install++There are a few tests and examples in the src directory.++TODOs:+-----++It may be possible (and worthwhile) to write a more general region monad that+is not tied to IO. I've had a brief look into this but right now I can't find+a clean and consistent way to abstract mutable references for different+monads.++It would also be nice to provide all the functions found in System.IO ideally+in such a way so the functions can be used with either Strings, ByteStrings or+something else.+I can't think of a clean way to do this either. Any suggestions would be+welcome.++Ivan Tomac 25/05/2008
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ src/Data/IORRef.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE DeriveDataTypeable #-}++-------------------------------------------------------------------------------+-- |+-- Module       : Data.IORRef+-- Copyright    : (c) Ivan Tomac 2008+-- License      : BSD3+--+-- Maintainer   : tomac `at` pacific `dot` net `dot` au+-- Stability    : experimental+--+-- Mutable references in the IOR monad.+--+-------------------------------------------------------------------------------++module Data.IORRef (+    IORRef++  , newIORRef+  , readIORRef+  , writeIORRef+  , modifyIORRef+) where++import Control.Monad.Trans++import Data.Generics+import Data.IORef++import System.IOR++-- | A value of type @'IORRef' r a@ is a mutable variable in region @r@,+-- containing a value of type @a@.++newtype IORRef r a+  = IORRef { unIORRef :: IORef a }+    deriving (Data, Eq, Typeable)++-- | Create a new 'IORRef' in region @r@.++newIORRef :: a -> IOR r rs (IORRef r a)+newIORRef = liftIO . fmap IORRef . newIORef++-- | Read the value of an 'IORRef'.++readIORRef :: RElem r' rs => IORRef r' a -> IOR r rs a +readIORRef = liftIO . readIORef . unIORRef++-- | Write a new value into an 'IORRef'.++writeIORRef :: RElem r' rs => IORRef r' a -> a -> IOR r rs ()+writeIORRef = (liftIO .) . writeIORef . unIORRef++-- | Mutate the contents of an 'IORRef'.++modifyIORRef :: RElem r' rs => IORRef r' a -> (a -> a) -> IOR r rs ()+modifyIORRef = (liftIO .) . modifyIORef . unIORRef
+ src/Example.hs view
@@ -0,0 +1,60 @@+module Main where++import Control.Monad.Trans++import System.IOR+import System.IOR.Resource++-------------------------------------------------------------------------------++newtype P+  = P String++openP  :: String -> IO P+closeP :: P -> IO ()+writeP :: P -> String -> IO ()++openP s = do+    putStrLn ("opening " ++ s)+    return   (P s)++closeP (P s) =+    putStrLn ("closing " ++ s)++writeP (P s) msg =+    putStrLn ("writing " ++ msg ++ " to " ++ s)++-------------------------------------------------------------------------------++newtype R r+  = R (Resource r P)++openR  :: String -> IOR r rs (R r)+closeR :: RElem r' rs => R r' -> IOR r rs ()+writeR :: RElem r' rs => R r' -> String -> IOR r rs ()++openR s = do+    p <- liftIO (openP s)+    fmap R (manage p closeP)++closeR (R r) =+    release r++writeR (R r) msg =+    liftIO (writeP (getResource r) msg)++-------------------------------------------------------------------------------++main :: IO ()+main = runIOR $ do+    r     <- getIORTag+    res1a <- openR "res1a"+    res1b <- openR "res1b"+    newIOR $ do+        res2a <- openR "res2a"+        res1c <- withIORTag r $ openR "res1c"+        writeR res1b "msg1"+        closeR res1b+        writeR res1c "msg2"+        writeR res2a "msg3"+    writeR res1a "msg4"
+ src/System/IOR.hs view
@@ -0,0 +1,35 @@+-------------------------------------------------------------------------------+-- |+-- Module       : System.IOR+-- Copyright    : (c) Ivan Tomac 2008+-- License      : BSD3+--+-- Maintainer   : tomac `at` pacific `dot` net `dot` au+-- Stability    : experimental+--+-- Region based resource management for the IO monad.+-- Based on the ideas and code from+-- <http://okmij.org/ftp/Haskell/regions.html>+--+-------------------------------------------------------------------------------++module System.IOR (+    -- * Type Classes and Auxiliary Data Types+    RElem+  , RCons+  , RNil++    -- * IOR Monad+  , IOR++  , runIOR+  , newIOR++    -- * Region Tags+  , IORTag++  , getIORTag+  , withIORTag+) where++import System.IOR.Internal
+ src/System/IOR/Internal.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE DeriveDataTypeable+           , EmptyDataDecls+           , ExistentialQuantification+           , FlexibleInstances+           , IncoherentInstances+           , MultiParamTypeClasses+           , PolymorphicComponents+           , TypeSynonymInstances+  #-}++-------------------------------------------------------------------------------+-- |+-- Module       : System.IOR.Internal+-- Copyright    : (c) Ivan Tomac 2008+-- License      : BSD3+--+-- Maintainer   : tomac `at` pacific `dot` net `dot` au+-- Stability    : experimental+--+-- IOR monad internals.+--+-------------------------------------------------------------------------------++module System.IOR.Internal (+    RElem+  , RCons+  , RNil++  , IOR (..)++  , runIOR+  , newIOR++  , IORTag (..)++  , getIORTag+  , withIORTag++  , Resource  (..)+  , Resource' (..)+) where++import Control.Applicative+import Control.Monad.Error+import Control.Monad.Trans++import Data.Generics+import Data.IORef++import System.IO.Error++class RElem r rs++data RCons r rs+data RNil++instance               RElem r (RCons r  rs)+instance RElem r rs => RElem r (RCons r' rs)++-- | 'IO' monad with support for region based resource allocation.+-- A computation of type @'IOR' r rs a@ wraps an action of type @'IO' a@+-- where @r@ is an unconstrained type variable indicating the current region+-- and @rs@ is a collection of all accessible regions within the computation.+--+-- 'IO' actions can be lifted into the 'IOR' monad using 'liftIO'.+-- It is safe to throw 'IOError'-s inside an 'IOR' computation.+-- Allocated resources will be released on exit automatically.++data IOR r rs a+  = IOR { unIOR :: IORTag r -> IO a }+    deriving (Data, Typeable)++instance Monad (IOR r rs) where+    return  = IOR . const . return+    m >>  n = IOR $ liftA2 (>>)  (unIOR m) (unIOR n)+    m >>= f = IOR $ liftA2 (>>=) (unIOR m) (flip $ unIOR . f)++    {-# INLINE return #-}+    {-# INLINE (>>)   #-}+    {-# INLINE (>>=)  #-}++instance Functor (IOR r rs) where+    fmap f m = IOR $ fmap f . unIOR m+    {-# INLINE fmap #-}++instance Applicative (IOR r rs) where+    pure  = return+    (<*>) = ap++instance MonadIO (IOR r rs) where+    liftIO = IOR . const+    {-# INLINE liftIO #-}++instance MonadFix (IOR r rs) where+    mfix f = IOR $ mfix . flip (unIOR . f)+    {-# INLINE mfix #-}++instance MonadError IOError (IOR r rs) where+    throwError = liftIO . throwError++    catchError m handler = IOR catchError'+        where+            catchError' = liftA2 catchError (unIOR m) handler'+            handler'    = flip (unIOR . handler)++run :: IOR r rs a -> IO a+run m = do+    ref <- newIORef (0, [])+    e   <- try $ unIOR m $ IORTag ref+    readIORef ref >>= mapM_ finalizeRes . snd+    either throwError return e+    where+        finalizeRes (Resource' r) = finalizer r++-- | Create the initial region, @r@, and run the computation returning a+-- value of type @'IO' a@.++runIOR :: IOR r (RCons r RNil) a -> IO a+runIOR = run++-- | Create a new region @r'@ inside @r@. All resources allocated in+-- @r'@ are only accessible from @r'@ and any of it's child regions.+-- On exit from the region, all allocated resources are automatically+-- released.++newIOR :: IOR r' (RCons r' rs) a -> IOR r rs a+newIOR = liftIO . run++type IdTag = Integer++-- | A region tag @'IORTag' r@ captures state of the region @r@ including all+-- currently allocated resources in @r@.++newtype IORTag r+  = IORTag { unIORTag :: IORef (IdTag, [Resource' r]) }+    deriving (Data, Eq, Typeable)++-- | Get the current region's tag.++getIORTag :: IOR r rs (IORTag r)+getIORTag = IOR return++-- | Temporarily change the current region from @r@ to @r'@. This allows+-- allocation of resources in @r'@.+--+-- @r'@ has to be one of the parent regions of @r@.++withIORTag :: RElem r' rs => IORTag r' -> IOR r' rs a -> IOR r rs a+withIORTag = (liftIO .) . flip unIOR++-- | @'Resource' r a@ wraps a resource of type @a@ so it can be managed+-- inside region @r@ and automatically released upon exit from @r@.++data Resource r a+  = Resource {+      -- | Extract @a@ from the 'Resource' wrapper.+        getResource :: a+      -- | Tag that uniquely identifies the resource in the current region.+      , idTag       :: IdTag+      -- | Reference to region state.+      , tagRef      :: IORef (IdTag, [Resource' r])+      -- | Finalizer for the resource.+      , finalizer   :: IO ()+    }+    deriving (Data, Typeable)++instance Eq (Resource r a) where+    r1 == r2 = idTag r1 == idTag r2++data Resource' r+  = forall a. Resource' {-# UNPACK #-} !(Resource r a)+    deriving Typeable++instance Eq (Resource' r) where+    Resource' r1 == Resource' r2 = idTag r1 == idTag r2
+ src/System/IOR/Resource.hs view
@@ -0,0 +1,58 @@+-------------------------------------------------------------------------------+-- |+-- Module       : System.IOR.Resource+-- Copyright    : (c) Ivan Tomac 2008+-- License      : BSD3+--+-- Maintainer   : tomac `at` pacific `dot` net `dot` au+-- Stability    : experimental+--+-- Resource management in the IOR monad.+--+-------------------------------------------------------------------------------++module System.IOR.Resource (+    Resource+  , getResource++  , manage+  , release+) where++import Control.Arrow+import Control.Monad.Trans++import Data.IORef+import Data.List++import System.IOR.Internal++-- | @'manage' a f@ will create a new 'Resource' wrapper around the+-- value of type @a@ in region @r@, given a finalizer @f@.+-- Each finalizer is guaranteed to automatically be called upon exit from+-- the region.+-- Finalizers are called in the last in, first out fashion. So the finalizer+-- of the very last resource allocated will be the first to get called.+--+-- Note that finalizers must not throw any errors. Failing to ensure that+-- all errors in a finalizer are handled may result in a resource leak.++manage :: a -> (a -> IO ()) -> IOR r rs (Resource r a)+manage a f = IOR (new . unIORTag)+    where+        new ref = do+            (c, rs) <- readIORef ref+            let r = Resource a c ref (f a)+            writeIORef ref (succ c, Resource' r : rs)+            return r++-- | @'release' res@ is used to force the resource @res@ to be+-- released immediately. Finalizer for @res@ will be called and removed+-- from the stack of finalizers in region @r'@.++release :: RElem r' rs => Resource r' a -> IOR r rs ()+release r = liftIO $ do+    readIORef ref >>= writeIORef ref . (id *** delete (Resource' r))+    finalizer r+    where+        ref = tagRef r
+ src/Test.hs view
@@ -0,0 +1,44 @@+module Main where++import Control.Applicative+import Control.Monad++import Data.IORRef++import System.IOR++gpair :: (RElem r0 rs, RElem r1 rs)+      => IORRef r0 a -> IORRef r1 b -> IOR r rs (IORRef r (a, b))++test1 :: IO ()+test2 :: IO ()+test3 :: IO ()+main  :: IO ()++gpair v w = liftA2 (,) (readIORRef v) (readIORRef w) >>= newIORRef++test1 = (print =<<) $ runIOR $ do+    r1 <- getIORTag+    a  <- newIORRef 1+    c  <- newIOR $ do+        v  <- readIORRef a+        b  <- newIORRef 2+        v' <- readIORRef b+        withIORTag r1 (newIORRef $ v + v')+    readIORRef c++test2 = (print =<<) $ runIOR $ do+    v <- newIORRef 1+    newIOR $ do+        w  <- newIORRef 2+        liftA2 (,)+               (gpair v w >>= readIORRef)+               (gpair w v >>= readIORRef)++test3 = (print =<<) $ runIOR $+    newIOR (newIORRef 1 >>= join gpair >>= readIORRef)++main = do+    test1+    test2+    test3