[Haskell-cafe] Bracket around every IO computation monad

Bas van Dijk v.dijk.bas at gmail.com
Mon Nov 15 20:05:31 EST 2010


On Tue, Nov 16, 2010 at 12:50 AM, Felipe Almeida Lessa
<felipe.lessa at gmail.com> wrote:
> That said, I don't know what 'regions' may do for you that the simple
> monad I presented doesn't.  Bas, what are the advantages?

My suggestion to use regions is based on an assumption which I'm not
sure is right. Mitar can answer that...

The assumption being that Mitar's Nerves are scarce resources (like
files for example). Meaning:

1) They have an 'open' operation yielding a 'handle' to the 'resource':

attach :: Nerve n -> IO (LiveNeuron n)

Compare this with:

openFile :: FilePath -> IOMode -> IO Handle

2) There are one or more operations defined on these handles:

someOperation :: LiveNeuron n -> IO ()

(I'm not sure Mitar actually has this...)

Compare this with:

hFileSize :: Handle -> IO Integer

3) They have a 'close' operation:

deattach :: LiveNeuron n -> IO ()

Compare with:

hClose :: Handle -> IO ()

4) It's important not to leave handles open (or in this case leave
nerves attached) when they don't need to be. In the case of files when
you leave a file open when you're not using it anymore, other
processes may not be able to use the file.

(I'm not sure this is a requirement for Nerves...)

5) It's an error to apply the operations to closed handles. In the
case of files, the following program is an error for example:

main = do
  h <- openFile "foo.txt" ReadMode
  hClose h
  hFileSize h -- error: h is already closed!

(Again, I'm not sure a similar requirement exists for LiveNeurons)

When these five requirements apply, you may find the regions package
useful. When you write a correct 'regional' layer around your scarce
resources like I showed earlier, regions will provide the following
guarantees:

* Resources are opened in a 'region'. When the region terminates
(whether normally or by raising an exception) all opened resources
will be closed automatically (this was Mitar's original requirement)

* It's a type-error to reference closed resources. This ensures no
operations can be applied to closed resources (deattached neurons).

Besides these guarantees the regions package is very expressive. It
allows you to nest regions inside each other and it allows you to
'duplicate' handles to ancestor regions. This allows you to use a
resource, which was opened in a nested region, in the parent of that
region, as in:

example :: IO ()
example = runRegionT $ do
            sn' <- runRegionT $ do
              sn <- saveAttach Nerve
              -- We can't 'return sn' because the neuron will be deattached
              -- when the regions terminates.
              -- Instead we have to 'duplicate' it which ensures it stays
              -- attached in the current region and will only be deattached
              -- in the parent region:
              sn' <- dup sn
              return sn'
            -- Back in the parent region we can safely apply some operation
            -- to the duplicated neuron:
            someSaveOperation sn'

To read more about regions, see both the API docs of regions:

http://hackage.haskell.org/package/regions

and some packages that define 'regional' layers over existing scarce
resources like:

* http://hackage.haskell.org/package/safer-file-handles
* http://hackage.haskell.org/package/regional-pointers
* http://hackage.haskell.org/package/usb-safe

Finally, I can also recommend Oleg's and Chung-chieh's paper about
"Lightweight monadic regions":

http://okmij.org/ftp/Haskell/regions.html#light-weight

Regards,

Bas

P.S.
Please apply /s/save/safe to my previously posted code. It's late...


More information about the Haskell-Cafe mailing list