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

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

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

MonadControlIO m => RegionControlIO m | |

RegionControlIO pr => RegionControlIO (RegionT s pr) |

# Duplication

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`

.

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

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

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

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