|Maintainer||Bas van Dijk <firstname.lastname@example.org>|
This modules implements a technique called "Lightweight monadic regions" invented by Oleg Kiselyov and Chung-chieh Shan
- data RegionT s pr α
- runRegionT :: RegionControlIO pr => (forall s. RegionT s pr α) -> pr α
- class MonadIO m => RegionControlIO m
- class Dup h where
- class InternalAncestorRegion pr cr => AncestorRegion pr cr
- data RootRegion α
- data LocalRegion sl s α
- data Local s
- liftCallCC :: (((α -> pr β) -> pr α) -> pr α) -> ((α -> RegionT s pr β) -> RegionT s pr α) -> RegionT s pr α
- mapRegionT :: (m α -> n β) -> RegionT s m α -> RegionT s n β
- liftCatch :: (pr α -> (e -> pr α) -> pr α) -> RegionT s pr α -> (e -> RegionT s pr α) -> RegionT s pr α
A monad transformer in which scarce resources can be opened. When the region terminates, all opened resources will be closed automatically. It's a type error to return an opened resource from the region. The latter ensures no I/O with closed resources is possible.
|InternalAncestorRegion pr cr => InternalAncestorRegion pr (RegionT s cr)|
|InternalAncestorRegion RootRegion (RegionT s m)|
|MonadTrans (RegionT s)|
|Monad pr => Monad (RegionT s pr)|
|Functor pr => Functor (RegionT s pr)|
|MonadFix pr => MonadFix (RegionT s pr)|
|MonadPlus pr => MonadPlus (RegionT s pr)|
|Applicative pr => Applicative (RegionT s pr)|
|Alternative pr => Alternative (RegionT s pr)|
|MonadIO pr => MonadIO (RegionT s pr)|
|RegionControlIO pr => RegionControlIO (RegionT s pr)|
|InternalAncestorRegion (LocalRegion sf s) (RegionT (Local s) m)|
|InternalAncestorRegion (RegionT s m) (RegionT (Local s) m)|
|InternalAncestorRegion (RegionT s m) (RegionT s m)|
|InternalAncestorRegion (RegionT (Local s) m) (RegionT s m)|
Execute a region inside its parent region
All resources which have been opened in the given region and which haven't been
dup, will be closed on exit from this function wether by
normal termination or by raising an exception.
Also all resources which have been duplicated to this region from a child region are closed on exit if they haven't been duplicated themselves.
Note the type variable
s of the region wich is only quantified over the region
itself. This ensures that all values, having a type containing
s, can not
be returned from this function. (Note the similarity with the
Note that it is possible to run a region inside another region.
Regions do not have an instance for
MonadControlIO since that would break the
safety guarantees. (Think about lifting
forkIO into a region!)
runRegionT and other operations on regions do need the ability to lift
control operations. This is where the
RegionControlIO class comes in. This
class is identical to
MonadControlIO but its
unsafeLiftControlIO method is
not exported by this module. So user can't accidentally break the safety.
h in the parent region. This
h will usually be some type of
For example, suppose you run the following region:
runRegionT $ do
Inside this region you run a nested child region like:
r1hDup <- runRegionT $ do
Now in this child region you open the resource
r1h <- open r1
...yielding the regional handle
r1h. Note that:
r1h :: RegionalHandle (RegionT cs (RegionT ps ppr))
cs is bound by the inner (child)
bound by the outer (parent)
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
escapes the inner region.
However, if you duplicate the regional handle you can safely return it.
r1hDup <- dup r1h return r1hDup
r1hDup :: RegionalHandle (RegionT ps ppr)
Back in the parent region you can safely operate on
Ancestor relation between regions
AncestorRegion class is used to relate the region in which a resource
was opened to the region in which it is used. Take the following operation from
safer-file-handles package as an example:
hFileSize :: (pr `AncestorRegion` cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Integer
AncestorRegion class defines the parent / child relationship between regions.
pr `AncestorRegion` cr
is satisfied if and only if
cr is a sequence of zero or more "
s) applied to
pr, in other words, if
cr is an (improper)
nested subregion of
The class constraint
InternalAncestorRegion pr cr serves two purposes. First, the
InternalAncestorRegion do the type-level recursion that implements
the relation specified above. Second, since it is not exported, user code cannot
add new instances of
AncestorRegion (as these would have to be made instances of
InternalAncestorRegion, too), effectively turning it into a closed class.
|InternalAncestorRegion pr cr => AncestorRegion pr cr|
The root region
RootRegion is the ancestor of any region.
It's primary purpose is to tag regional handles which don't have an associated
finalizer. For example the standard file handles
which are opened on program startup and which shouldn't be closed when a region
terminates. Another example is the
nullPtr which is a memory pointer which
doesn't point to any allocated memory so doesn't need to be freed.
LocalRegion is used to tag regional handles which are created locally.
An example is the
LocalPtr in the
alloca function from the
alloca :: (Storable a, MonadControlIO pr) => (forall sl. LocalPtr a (
LocalRegionsl s) -> RegionT (
Locals) pr b) -> RegionT s pr b
The finalisation of the
LocalPtr is not performed by the
regions library but
is handled locally by
The type variable
sl, which is only quantified over the continuation, ensures
that locally opened resources don't escape.
A type used to tag regions in which locally created handles (handles tagged with
LocalRegion) can be used.
Note than any handle which is created in a
RegionT (Local s) can be used
outside that region (
RegionT s) and visa versa
Utilities for writing monadic instances
callCC operation to the new monad.
Transform the computation inside a region.