regions 0.1.0.1 → 0.2
raw patch · 4 files changed
+128/−94 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Control.Monad.Trans.Region.Internal: class (Resource resource) => Dup α resource
- Control.Monad.Trans.Region.Internal: class (Monad pr, Monad cr) => ParentOf pr cr
- Control.Monad.Trans.Region.Internal: class Resource resource where { data family Handle resource :: *; }
- Control.Monad.Trans.Region.Internal: closeResource :: (Resource resource) => Handle resource -> IO ()
- Control.Monad.Trans.Region.Internal: data RegionT resource s pr :: (* -> *) α
- Control.Monad.Trans.Region.Internal: data RegionalHandle resource r :: (* -> *)
- Control.Monad.Trans.Region.Internal: dup :: (Dup α resource, MonadCatchIO ppr) => α (RegionT resource cs (RegionT resource ps ppr)) -> RegionT resource cs (RegionT resource ps ppr) (α (RegionT resource ps ppr))
- Control.Monad.Trans.Region.Internal: forkTopRegion :: (Resource resource, MonadIO pr) => TopRegion resource s () -> RegionT resource s pr ThreadId
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Alternative pr) => Alternative (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Applicative pr) => Applicative (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Functor pr) => Functor (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Monad cr, TypeCast2 cr (RegionT resource s pcr), ParentOf pr pcr) => ParentOf pr cr
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Monad pr) => Monad (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Monad r) => ParentOf r r
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (MonadCatchIO pr) => MonadCatchIO (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (MonadFix pr) => MonadFix (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (MonadIO pr) => MonadIO (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (MonadPlus pr) => MonadPlus (RegionT resource s pr)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (Resource resource) => Dup (RegionalHandle resource) resource
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (TypeCast2' () a b) => TypeCast2 a b
- Control.Monad.Trans.Region.Internal: instance [overlap ok] (TypeCast2'' t a b) => TypeCast2' t a b
- Control.Monad.Trans.Region.Internal: instance [overlap ok] MonadTrans (RegionT resource s)
- Control.Monad.Trans.Region.Internal: instance [overlap ok] TypeCast2'' () a a
- Control.Monad.Trans.Region.Internal: internalHandle :: RegionalHandle resource r -> Handle resource
- Control.Monad.Trans.Region.Internal: liftCatch :: (pr α -> (e -> pr α) -> pr α) -> RegionT resource s pr α -> (e -> RegionT resource s pr α) -> RegionT resource s pr α
- Control.Monad.Trans.Region.Internal: mapRegionT :: (m α -> n β) -> RegionT resource s m α -> RegionT resource s n β
- Control.Monad.Trans.Region.Internal: open :: (Resource resource, MonadCatchIO pr) => resource -> RegionT resource s pr (RegionalHandle resource (RegionT resource s pr))
- Control.Monad.Trans.Region.Internal: openResource :: (Resource resource) => resource -> IO (Handle resource)
- Control.Monad.Trans.Region.Internal: runRegionT :: (Resource resource, MonadCatchIO pr) => (forall s. RegionT resource s pr α) -> pr α
- Control.Monad.Trans.Region.Internal: runTopRegion :: (Resource resource) => (forall s. TopRegion resource s α) -> IO α
- Control.Monad.Trans.Region.Internal: type TopRegion resource s = RegionT resource s IO
- Control.Monad.Trans.Region.Internal: with :: (Resource resource, MonadCatchIO pr) => resource -> (forall s. RegionalHandle resource (RegionT resource s pr) -> RegionT resource s pr α) -> pr α
+ Control.Monad.Trans.Region: liftCatch :: (pr α -> (e -> pr α) -> pr α) -> RegionT s pr α -> (e -> RegionT s pr α) -> RegionT s pr α
+ Control.Monad.Trans.Region: mapRegionT :: (m α -> n β) -> RegionT s m α -> RegionT s n β
+ Control.Monad.Trans.Region.Unsafe: class Dup α
+ Control.Monad.Trans.Region.Unsafe: class (Monad pr, Monad cr) => ParentOf pr cr
+ Control.Monad.Trans.Region.Unsafe: class Resource resource where { data family Handle resource :: *; }
+ Control.Monad.Trans.Region.Unsafe: closeResource :: (Resource resource) => Handle resource -> IO ()
+ Control.Monad.Trans.Region.Unsafe: dup :: (Dup α, MonadCatchIO ppr) => α (RegionT cs (RegionT ps ppr)) -> RegionT cs (RegionT ps ppr) (α (RegionT ps ppr))
+ Control.Monad.Trans.Region.Unsafe: internalHandle :: RegionalHandle resource r -> Handle resource
+ Control.Monad.Trans.Region.Unsafe: openResource :: (Resource resource) => resource -> IO (Handle resource)
- Control.Monad.Trans.Region: data RegionT resource s pr :: (* -> *) α
+ Control.Monad.Trans.Region: data RegionT s pr :: (* -> *) α
- Control.Monad.Trans.Region: dup :: (Dup α resource, MonadCatchIO ppr) => α (RegionT resource cs (RegionT resource ps ppr)) -> RegionT resource cs (RegionT resource ps ppr) (α (RegionT resource ps ppr))
+ Control.Monad.Trans.Region: dup :: (Dup α, MonadCatchIO ppr) => α (RegionT cs (RegionT ps ppr)) -> RegionT cs (RegionT ps ppr) (α (RegionT ps ppr))
- Control.Monad.Trans.Region: forkTopRegion :: (Resource resource, MonadIO pr) => TopRegion resource s () -> RegionT resource s pr ThreadId
+ Control.Monad.Trans.Region: forkTopRegion :: (MonadIO pr) => TopRegion s () -> RegionT s pr ThreadId
- Control.Monad.Trans.Region: open :: (Resource resource, MonadCatchIO pr) => resource -> RegionT resource s pr (RegionalHandle resource (RegionT resource s pr))
+ Control.Monad.Trans.Region: open :: (Resource resource, MonadCatchIO pr) => resource -> RegionT s pr (RegionalHandle resource (RegionT s pr))
- Control.Monad.Trans.Region: runRegionT :: (Resource resource, MonadCatchIO pr) => (forall s. RegionT resource s pr α) -> pr α
+ Control.Monad.Trans.Region: runRegionT :: (MonadCatchIO pr) => (forall s. RegionT s pr α) -> pr α
- Control.Monad.Trans.Region: runTopRegion :: (Resource resource) => (forall s. TopRegion resource s α) -> IO α
+ Control.Monad.Trans.Region: runTopRegion :: (forall s. TopRegion s α) -> IO α
- Control.Monad.Trans.Region: type TopRegion resource s = RegionT resource s IO
+ Control.Monad.Trans.Region: type TopRegion s = RegionT s IO
- Control.Monad.Trans.Region: with :: (Resource resource, MonadCatchIO pr) => resource -> (forall s. RegionalHandle resource (RegionT resource s pr) -> RegionT resource s pr α) -> pr α
+ Control.Monad.Trans.Region: with :: (Resource resource, MonadCatchIO pr) => resource -> (forall s. RegionalHandle resource (RegionT s pr) -> RegionT s pr α) -> pr α
Files
- Control/Monad/Trans/Region.hs +5/−0
- Control/Monad/Trans/Region/Internal.hs +72/−89
- Control/Monad/Trans/Region/Unsafe.hs +44/−0
- regions.cabal +7/−5
Control/Monad/Trans/Region.hs view
@@ -32,6 +32,11 @@ -- * Duplication , dup++ -- * Handy functions for writing monadic instances+ , mapRegionT+ , liftCatch+ -- | /TODO: define and export: /@liftCallCC@ ) where import Control.Monad.Trans.Region.Internal
Control/Monad/Trans/Region/Internal.hs view
@@ -11,6 +11,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ExistentialQuantification #-} ------------------------------------------------------------------------------- -- |@@ -24,17 +25,8 @@ -- -- See: <http://okmij.org/ftp/Haskell/regions.html#light-weight> ----- This module should only be used by library authors wishing to allow their--- users to open their type of resources in a region. It should not be used by--- end-users directly!------ To create a module or library that allows your users to open your type of--- resources in a region is to define an instance for 'Resource' for your type--- of resource.------ Make sure not to re-export anything from this module. Either re-export things--- from @Control.Monad.Trans.Region@ or tell your users to import that module--- directly.+-- This is an internal module not intended to be exported.+-- Use @Control.Monad.Trans.Region@ or @Control.Monad.Trans.Region.Unsafe@. -- -------------------------------------------------------------------------------- @@ -85,7 +77,7 @@ import Control.Concurrent ( forkIO, ThreadId ) import Control.Applicative ( Applicative, Alternative ) import Control.Monad ( Monad, return, (>>=), fail- , (>>), when, liftM2, mapM_+ , (>>), when, liftM2, mapM_, forM_ , Functor , MonadPlus )@@ -135,15 +127,15 @@ -- * Regions -------------------------------------------------------------------------------- -{-| A monad transformer in which resources of type @resource@ can be opened+{-| A monad transformer in which scarce resources can be opened which are automatically closed when the region terminates. Note that regions can be nested. @pr@ (for parent region) is a monad which is usually the region which is running this region. However when you are running a 'TopRegion' the parent region will be 'IO'. -}-newtype RegionT resource s (pr ∷ * → *) α = RegionT- { unRegionT :: ReaderT (IORef [Opened resource]) pr α }+newtype RegionT s (pr ∷ * → *) α = RegionT+ { unRegionT ∷ ReaderT (IORef [SomeOpenedResource]) pr α } deriving ( Functor , Applicative@@ -156,10 +148,19 @@ , MonadCatchIO ) -{-| An @Opened resource@ is an internal data type which associates a handle to-the resource with a reference count. Handles are reference counted because they-may be /duplicated/ to a parent region using 'dup'.+-- | An internally used datatype which adds an existential wrapper around+-- 'OpenedResource' to hide the @resource@ type. If the @resource@ type is not+-- hidden, regions must be parameterized by it. This is undesirable because then+-- only one type of resource can be opened in a region. The following allows all+-- kinds of resources to be opened in a single region as long as they have an+-- instance for 'Resource'.+data SomeOpenedResource = ∀ resource. Resource resource+ ⇒ Some (Opened resource) +{-| An @Opened resource@ is an internally used data type which associates a+handle to the resource with a reference count. Handles are reference counted+because they may be /duplicated/ to a parent region using 'dup'.+ The reference count is: * Initialized at 1 in 'open'.@@ -175,16 +176,17 @@ , refCntIORef ∷ IORef Int } --- | Internal function that atomically decrements the reference count that is--- stored in the given @IORef@. The function returns the decremented reference--- count.+{-| Internally used function that atomically decrements the reference count that+is stored in the given @IORef@. The function returns the decremented reference+count.+-} decrement ∷ IORef Int → IO Int decrement ioRef = atomicModifyIORef ioRef $ \refCnt → let predRefCnt = pred refCnt in (predRefCnt, predRefCnt) --- | Internal function that atomically increments the reference count that is--- stored in the given @IORef@.+-- | Internally used function that atomically increments the reference count that+-- is stored in the given @IORef@. increment ∷ IORef Int → IO () increment ioRef = atomicModifyIORef ioRef $ \refCnt → (succ refCnt, ())@@ -216,10 +218,7 @@ Note that it is possible to run a region inside another region. -}-runRegionT ∷ (Resource resource, MonadCatchIO pr)- ⇒ (∀ s. RegionT resource s pr α) -- ^ Region you wish to execute.- → pr α -- ^ Computation in the parent region which executes the given- -- region.+runRegionT ∷ MonadCatchIO pr ⇒ (∀ s. RegionT s pr α) → pr α runRegionT m = runRegionWith [] m {-| A region which has 'IO' as its parent region which enables it to be:@@ -228,16 +227,13 @@ * concurrently executed in a new thread by 'forkTopRegion'. -}-type TopRegion resource s = RegionT resource s IO+type TopRegion s = RegionT s IO {-| Convenience funtion for running a /top-level/ region in 'IO'. Note that: @runTopRegion = 'runRegionT'@ -}-runTopRegion ∷ Resource resource- ⇒ (∀ s. TopRegion resource s α)- -- ^ /Top-level/ region you wish to execute.- → IO α -- ^ An @IO@ computation which executes the given region.+runTopRegion ∷ (∀ s. TopRegion s α) → IO α runTopRegion = runRegionT {-| Return a region which executes the given /top-level/ region in a new thread.@@ -260,12 +256,7 @@ thread are closed only when the current thread or the forked thread terminates whichever comes /last/. -}-forkTopRegion ∷ (Resource resource, MonadIO pr)- ⇒ TopRegion resource s () -- ^ /Top-level/ region you wish to- -- execute in a new thread.- → RegionT resource s pr ThreadId- -- ^ A regional computation that executes the given region in a- -- new thread and returns the @ThreadId@ of this new thread.+forkTopRegion ∷ MonadIO pr ⇒ TopRegion s () → RegionT s pr ThreadId forkTopRegion m = RegionT $ do -- Get the list of currently opened resources in this region: openedResourcesIORef ← ask@@ -275,7 +266,8 @@ -- Increment the reference count of each opened resource so that -- when the current region terminates the resources will stay -- open in the to be created thread:- mapM_ (increment ∘ refCntIORef) openedResources+ forM_ openedResources $ \(Some openedResource) →+ increment $ refCntIORef $ openedResource -- Fork a new thread that will concurrently run the given region -- on the opened resources of the current region:@@ -289,12 +281,11 @@ -- count will never reach 0!) forkIO $ runRegionWith openedResources m --- | Internal function that actually runs the region on the given list of opened--- resources.-runRegionWith ∷ ∀ resource s pr α.- (MonadCatchIO pr, Resource resource)- ⇒ [Opened resource]- → RegionT resource s pr α+-- | Internally used function that actually runs the region on the given list of+-- opened resources.+runRegionWith ∷ ∀ s pr α. MonadCatchIO pr+ ⇒ [SomeOpenedResource]+ → RegionT s pr α → pr α runRegionWith openedResources m = bracket@@ -310,7 +301,7 @@ (runReaderT $ unRegionT m) where- end (Opened {openedHandle, refCntIORef}) = do+ end (Some (Opened {openedHandle, refCntIORef})) = do refCnt ← decrement refCntIORef when (refCnt ≡ 0) $ closeResource openedHandle @@ -326,8 +317,9 @@ {-| Get the internal handle from the regional handle. -/Warning:/ This function should not be exported to end-users because it allows-them to close the handle manually!+/Warning:/ This function should not be exported to or used by end-users because+it allows them to close the handle manually, which will defeat the+safety-guarantees that this package provides! /Tip:/ If you enable the @ViewPatterns@ language extension you can use @internalHandle@ as a view-pattern as in the following example from the@@ -353,11 +345,9 @@ it was created you have to /duplicate/ the handle by applying 'dup' to it. -} open ∷ (Resource resource, MonadCatchIO pr)- ⇒ resource -- ^ The resource you wish to open.- → RegionT resource s pr- (RegionalHandle resource (RegionT resource s pr))- -- ^ A regional computation that returns a regional handle to the given- -- opened resource parameterized by the region itself.+ ⇒ resource+ → RegionT s pr+ (RegionalHandle resource (RegionT s pr)) open resource = block $ do -- Create a new opened resource by actually opening the resource and -- intializing the reference count to 1:@@ -385,23 +375,17 @@ Note that: @with dev f = @'runRegionT'@ (@'open'@ dev @'>>='@ f)@ -} with ∷ (Resource resource, MonadCatchIO pr)- ⇒ resource -- ^ The resource you wish to open.- → (∀ s. RegionalHandle resource (RegionT resource s pr)- → RegionT resource s pr α- ) -- ^ Continuation function.- → pr α -- ^ A computation which runs a child region which opens the given- -- resource and applies the given continuation function to the- -- resulting regional handle.+ ⇒ resource+ → (∀ s. RegionalHandle resource (RegionT s pr) → RegionT s pr α)+ → pr α with resource f = runRegionT $ open resource >>= f --- | Internal function to /register/ the given opened resource by consing it to--- the list of opened resources of the region.-register ∷ (Resource resource, MonadIO pr)- ⇒ Opened resource- → RegionT resource s pr ()+-- | Internally used function to /register/ the given opened resource by consing+-- it to the list of opened resources of the region.+register ∷ (Resource resource, MonadIO pr) ⇒ Opened resource → RegionT s pr () register openedResource = RegionT $ do openedResourcesIORef ← ask- liftIO $ modifyIORef openedResourcesIORef (openedResource:)+ liftIO $ modifyIORef openedResourcesIORef (Some openedResource:) --------------------------------------------------------------------------------@@ -432,12 +416,12 @@ ...yielding the regional handle @r1h@. Note that: -@r1h :: RegionalHandle resource (RegionT resource cs (RegionT resource ps ppr))@+@r1h :: RegionalHandle resource (RegionT cs (RegionT ps ppr))@ where @cs@ is bound by the inner (child) @runRegionT@ and @ps@ is bound by the outer (parent) @runRegionT@. -Suppose you want to use the resulting regional handle @r1h@ in the /parent/ device+Suppose you want to use the resulting regional handle @r1h@ in the /parent/ region. You can't simply @return r1h@ because then the type variable @cs@, escapes the inner region. @@ -448,20 +432,19 @@ return r1hDup @ -Note that @r1hDup :: RegionalHandle resource (RegionT resource ps ppr)@+Note that @r1hDup :: RegionalHandle resource (RegionT ps ppr)@ Back in the parent region you can safely operate on @r1hDup@. -}-class Resource resource ⇒ Dup α resource where- dup ∷ (MonadCatchIO ppr)- ⇒ α (RegionT resource cs (RegionT resource ps ppr))- -- ^ Something created in a child region.- → RegionT resource cs (RegionT resource ps ppr)- (α (RegionT resource ps ppr))- -- ^ The child region which returns the thing which can now be used in- -- the parent region.+class Dup α where+ dup ∷ MonadCatchIO ppr+ ⇒ α (RegionT cs (RegionT ps ppr))+ → RegionT cs (RegionT ps ppr)+ (α (RegionT ps ppr))+ -- The child region which returns the thing which can now be used in+ -- the parent region. -instance Resource resource ⇒ Dup (RegionalHandle resource) resource where+instance Resource resource ⇒ Dup (RegionalHandle resource) where dup (RegionalHandle openedResource) = block $ do -- Increment the reference count of the given opened resource so that it -- won't get closed when the current region terminates:@@ -495,14 +478,14 @@ -- liftCallCC callCC f = RegionT $ ??? -- | Transform the computation inside a region.-mapRegionT ∷ (m α → n β) → RegionT resource s m α → RegionT resource s n β+mapRegionT ∷ (m α → n β) → RegionT s m α → RegionT s n β mapRegionT f = RegionT ∘ mapReaderT f ∘ unRegionT -- | Lift a 'catchError' operation to the new monad.-liftCatch ∷ (pr α → (e → pr α) → pr α) -- ^ @catch@ on the argument monad.- → RegionT resource s pr α -- ^ Computation to attempt.- → (e → RegionT resource s pr α) -- ^ Exception handler.- → RegionT resource s pr α+liftCatch ∷ (pr α → (e → pr α) → pr α) -- ^ @catch@ on the argument monad.+ → RegionT s pr α -- ^ Computation to attempt.+ → (e → RegionT s pr α) -- ^ Exception handler.+ → RegionT s pr α liftCatch f m h = RegionT $ Reader.liftCatch f (unRegionT m) (unRegionT ∘ h) @@ -515,16 +498,16 @@ A region is the parent of another region if they're either equivalent like: @-RegionT resource ps pr \`ParentOf\` RegionT resource ps pr+RegionT ps pr \`ParentOf\` RegionT ps pr @ or if it is the parent of the parent of the child like: @-RegionT resource ps ppr \`ParentOf\` RegionT resource cs- (RegionT resource pcs- (RegionT resource ppcs- (RegionT resource ps ppr)))+RegionT ps ppr \`ParentOf\` RegionT cs+ (RegionT pcs+ (RegionT ppcs+ (RegionT ps ppr))) @ -} class (Monad pr, Monad cr) ⇒ pr `ParentOf` cr@@ -532,7 +515,7 @@ instance Monad r ⇒ ParentOf r r instance ( Monad cr- , cr `TypeCast2` RegionT resource s pcr+ , cr `TypeCast2` RegionT s pcr , pr `ParentOf` pcr ) ⇒ ParentOf pr cr
+ Control/Monad/Trans/Region/Unsafe.hs view
@@ -0,0 +1,44 @@+-------------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Region.Unsafe+-- Copyright : (c) 2009 Bas van Dijk+-- License : BSD3 (see the file LICENSE)+-- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>+--+-- /WARNING:/ This module should /not/ be used by end-users directly because it+-- allows access to the 'internalHandle' of a resource which enables them to+-- close the resource manually, which will defeat the safety-guarantees that+-- this package provides!+--+-- This module should /only/ be used by library authors wishing to allow their+-- end-users to open their resources in a region.+--+-- The only thing to do, to create a module or library that allows your users to+-- open your resources in a region, is to define an instance for 'Resource' for+-- your type of resource.+--+-- Make sure not to re-export anything from this module. Either re-export things+-- from @Control.Monad.Trans.Region@ or tell your users to import that module+-- directly.+--+--------------------------------------------------------------------------------++module Control.Monad.Trans.Region.Unsafe+ ( -- * Scarce resources+ Resource+ , Handle+ , openResource+ , closeResource++ -- * Accessing the internal handle of a resource.+ , internalHandle++ -- * Duplication+ , Dup+ , dup++ -- * Parent/child relationship between regions.+ , ParentOf+ ) where++import Control.Monad.Trans.Region.Internal
regions.cabal view
@@ -1,5 +1,5 @@ name: regions-version: 0.1.0.1+version: 0.2 cabal-version: >=1.6 build-type: Simple license: BSD3@@ -35,9 +35,9 @@ . - * @Control.Monad.Trans.Region.Internal@ should only be used by library authors+ * @Control.Monad.Trans.Region.Unsafe@ should only be used by library authors wishing to allow their users to open their type of resources in a region. It- should not be used by end-users directly!+ should /not/ be used by end-users directly! . @@ -51,7 +51,8 @@ . - For an example of how to use this library see the @usb-safe@ package.+ For an example of how to use this library see the @safer-file-handles@ or+ @usb-safe@ packages. source-repository head Type: darcs@@ -64,4 +65,5 @@ , transformers >= 0.1.4 && < 0.2 , MonadCatchIO-transformers == 0.0.2.* exposed-modules: Control.Monad.Trans.Region- , Control.Monad.Trans.Region.Internal+ , Control.Monad.Trans.Region.Unsafe+ other-modules: Control.Monad.Trans.Region.Internal