regions-0.11: Provides the region monad for safely opening and working with scarce resources.

MaintainerBas van Dijk <>




This modules implements a technique called "Lightweight monadic regions" invented by Oleg Kiselyov and Chung-chieh Shan




data RegionT s pr α Source

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) 

Running regions

runRegionT :: RegionControlIO pr => (forall s. RegionT s pr α) -> pr αSource

Execute a region inside its parent region pr.

All resources which have been opened in the given region and which haven't been duplicated using 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 ST monad.)

Note that it is possible to run a region inside another region.

class MonadIO m => RegionControlIO m Source

Regions do not have an instance for MonadControlIO since that would break the safety guarantees. (Think about lifting forkIO into a region!)

However 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.

Note that a RegionT is an instance of this class. For the rest there is a catch-all instance MonadControlIO m => RegionControlIO m.


class Dup h whereSource

Duplicate an h in the parent region. This h will usually be some type of regional handle.

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 r1:

        r1h <- open r1

...yielding the regional handle r1h. Note that:

r1h :: RegionalHandle (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 region. You can't simply return r1h because then the type variable cs, escapes the inner region.

However, if you duplicate the regional handle you can safely return it.

        r1hDup <- dup r1h
        return r1hDup

Note that r1hDup :: RegionalHandle (RegionT ps ppr)

Back in the parent region you can safely operate on r1hDup.


dup :: MonadIO ppr => h (RegionT cs (RegionT ps ppr)) -> RegionT cs (RegionT ps ppr) (h (RegionT ps ppr))Source


Ancestor relation between regions

class InternalAncestorRegion pr cr => AncestorRegion pr cr Source

The 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 the safer-file-handles package as an example:

hFileSize :: (pr `AncestorRegion` cr, MonadIO cr) => RegionalFileHandle ioMode pr -> cr Integer

The AncestorRegion class defines the parent / child relationship between regions. The constraint

    pr `AncestorRegion` cr

is satisfied if and only if cr is a sequence of zero or more "RegionT s" (with varying s) applied to pr, in other words, if cr is an (improper) nested subregion of pr.

The class constraint InternalAncestorRegion pr cr serves two purposes. First, the instances of 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 

Special regions

The root region

data RootRegion α Source

The 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 stdin, stdout and stderr 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.


InternalAncestorRegion RootRegion (RegionT s m) 

Local regions

data LocalRegion sl s α Source

A LocalRegion is used to tag regional handles which are created locally.

An example is the LocalPtr in the alloca function from the regional-pointers package:

alloca :: (Storable a, MonadControlIO pr)
       => (forall sl. LocalPtr a (LocalRegion sl s) -> RegionT (Local s) pr b)
       -> RegionT s pr b

The finalisation of the LocalPtr is not performed by the regions library but is handled locally by alloca instead.

The type variable sl, which is only quantified over the continuation, ensures that locally opened resources don't escape.


InternalAncestorRegion (LocalRegion sf s) (RegionT (Local s) m) 

data Local s Source

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 (except for LocalRegion-tagged handles).


InternalAncestorRegion (LocalRegion sf s) (RegionT (Local s) m) 
InternalAncestorRegion (RegionT s m) (RegionT (Local s) m) 
InternalAncestorRegion (RegionT (Local s) m) (RegionT s m) 

Utilities for writing monadic instances

liftCallCC :: (((α -> pr β) -> pr α) -> pr α) -> ((α -> RegionT s pr β) -> RegionT s pr α) -> RegionT s pr αSource

Lift a callCC operation to the new monad.

mapRegionT :: (m α -> n β) -> RegionT s m α -> RegionT s n βSource

Transform the computation inside a region.

liftCatch :: (pr α -> (e -> pr α) -> pr α) -> RegionT s pr α -> (e -> RegionT s pr α) -> RegionT s pr αSource

Lift a catchError operation to the new monad.