Maintainer | Bas van Dijk <v.dijk.bas@gmail.com> |
---|

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

- data RegionT s pr α
- runRegionT :: MonadPeelIO pr => (forall s. RegionT s pr α) -> pr α
- class Dup α where
- class InternalAncestorRegion pr cr => AncestorRegion pr cr
- data RootRegion α
- 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 α

# Regions

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) | |

MonadPeelIO pr => MonadPeelIO (RegionT s pr) | |

MonadIO pr => MonadIO (RegionT s pr) | |

InternalAncestorRegion (RegionT s m) (RegionT s m) |

# Running regions

runRegionT :: MonadPeelIO 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.

# Duplication

Duplicate an `α`

in the parent region. This `α`

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`

.

# 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 "

"
(with varying `RegionT`

s`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 |

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) |

# Handy functions 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.