[Haskell-cafe] Updates in the regions library

Bas van Dijk v.dijk.bas at gmail.com
Tue Jun 8 07:39:08 EDT 2010


Hi Arie (and others who are interested in the regions library),

I would like to let you know that I'm working on a new version of my
regions package and its reverse dependencies:
regions-monadsfd, regions-monadstf,
safer-file-handles, regional-pointers and usb-safe.

The major change is that I removed the Resource class:

class Resource resource where
    data Handle resource ∷ *
    open ∷ resource → IO (Handle resource)
    close ∷ Handle resource → IO ()

in favor of a much more simple interface, namely:

-- | An 'IO' computation that closes or finalizes a resource. For example
-- 'hClose' or 'free'.
type CloseAction = IO ()

-- | A handle to a 'CloseAction' that allows you to duplicate the action to a
-- parent region using 'dup'.
newtype CloseHandle (r ∷ * → *)

-- | Register the 'CloseAction' in the region. When the region terminates all
-- registered close actions will be perfomed if they're not duplicated to a
-- parent region.
register ∷ MonadIO pr ⇒ CloseAction → RegionT s pr (CloseHandle (RegionT s pr))

Here's an example how to use this new interface from my updated
regional-pointers package:

import qualified Foreign.Marshal.Alloc as FMA

-- | A regional handle to memory. This should provide a safer replacement for
-- Foreign.Ptr.Ptr
data RegionalPtr α (r ∷ * → *) = RegionalPtr (Ptr α) (CloseHandle r)

{-| Allocates the given number of bytes and returns a
regional pointer to them.

This should provide a safer replacement for:
Foreign.Marshal.Alloc.mallocBytes.
-}
mallocBytes ∷ MonadCatchIO pr
            ⇒ Int
            → RegionT s pr (RegionalPtr α (RegionT s pr))
mallocBytes size = block $ do
                     ptr ← liftIO $ FMA.mallocBytes size
                     let closeAction = free ptr
                     ch ← register closeAction
                     return $ RegionalPtr ptr ch

And here's an example from safer-file-handles:

-- | A regional handle to an opened file parameterized by the 'IOMode' in which
-- you opened the file and the region in which it was created.
data RegionalFileHandle ioMode (r ∷ * → *) =
    RegionalFileHandle (Handle ioMode) (CloseHandle r)

openFile ∷ MonadCatchIO pr
         ⇒ FilePath
         → IOMode ioMode
         → RegionT s pr
             (RegionalFileHandle ioMode (RegionT s pr))
openFile = openNormal E.openFile

-- | Opens a file in binary mode then yields a regional handle to it. This
-- provides a safer replacement for System.IO.openBinaryFile.
openBinaryFile ∷ MonadCatchIO pr
               ⇒ FilePath
               → IOMode ioMode
               → RegionT s pr
                   (RegionalFileHandle ioMode (RegionT s pr))
openBinaryFile = openNormal E.openBinaryFile

openNormal ∷ MonadCatchIO pr
           ⇒ (FilePath → IOMode ioMode → IO (E.Handle ioMode))
           → FilePath
           → IOMode ioMode
           → RegionT s pr
               (RegionalFileHandle ioMode (RegionT s pr))
openNormal open filePath ioMode = block $ do
  h ← liftIO $ open filePath ioMode
  let closeAction = sanitizeIOError $ hClose h
  ch ← register closeAction
  return $ RegionalFileHandle h ch

I haven't released it yet because I need to write some more
documentation. However all the repositories contain the new code:

darcs get http://code.haskell.org/~basvandijk/code/regions
darcs get http://code.haskell.org/~basvandijk/code/regions-monadsfd
darcs get http://code.haskell.org/~basvandijk/code/regions-monadstf
darcs get http://code.haskell.org/~basvandijk/code/regional-pointers
darcs get http://code.haskell.org/~basvandijk/code/safer-file-handles
darcs get http://code.haskell.org/~basvandijk/code/usb-safe

and some examples:

darcs get http://code.haskell.org/~basvandijk/code/usb-safe-examples
darcs get http://code.haskell.org/~basvandijk/code/safer-file-handles-examples
darcs get http://code.haskell.org/~basvandijk/code/usb-safe-and-safer-file-handles-examples

Regards,

Bas

On Wed, Jun 2, 2010 at 3:13 PM, Bas van Dijk <v.dijk.bas at gmail.com> wrote:
> On Wed, Jun 2, 2010 at 2:57 PM, Arie Peterson <ariep at xs4all.nl> wrote:
>>> On Wed, Jun 2, 2010 at 2:28 PM, Bas van Dijk <v.dijk.bas at gmail.com>
>> wrote:
>>>> Before answering your questions I would like to make sure I understand
>>>> your Resource type. When I want to create a memory Resource for
>>>> example is the following what you have in mind?
>>>>
>>>> {-# LANGUAGE Rank2Types #-}
>>>>
>>>> -- from base:
>>>> import Foreign.Ptr ( Ptr )
>>>> import Foreign.Marshal.Alloc ( mallocBytes, free )
>>>>
>>>> -- from transformers:
>>>> import Control.Monad.IO.Class ( liftIO )
>>>>
>>>> -- from MonadCatchIO-transformers:
>>>> import Control.Monad.CatchIO ( MonadCatchIO, bracket )
>>>>
>>>> newtype Resource cap m = Resource { with :: forall a. (cap -> m a) -> m
>>>> a }
>>>>
>>>> type Memory m a = Resource (Ptr a) m
>>>>
>>>> memory :: MonadCatchIO m => Int -> Memory m a
>>>> memory size = Resource $ bracket (liftIO $ mallocBytes size) (liftIO .
>>>> free)
>>
>> Yes, exactly. I also create type aliases for resources providing a
>> specific capability.
>>
>> On Wed, 2 Jun 2010 14:45:08 +0200, Bas van Dijk <v.dijk.bas at gmail.com>
>> wrote:
>>> The previous can also be generalized using my Resource class:
>>>
>>> -- from regions:
>>> import qualified Control.Resource as C ( Resource(..) )
>>>
>>> resource :: (MonadCatchIO m, C.Resource resource)
>>>          => resource -> Resource (C.Handle resource) m
>>> resource r = Resource $ bracket (liftIO $ C.open r) (liftIO . C.close)
>>
>> Yes, definitely.
>>
>> (This is not a literal generalisation of the 'memory' function, unless you
>> make 'Int' an instance of 'C.Resource'; one would probably create a special
>> type 'data Memory = Memory Int' instead. This is the difference I alluded
>> to in my earlier email.)
>
> Indeed.
>
> My regional-pointers package provides this Memory type and an instance
> for Resource:
> http://hackage.haskell.org/packages/archive/regional-pointers/0.1.0.2/doc/html/Foreign-Ptr-Region.html#t%3AMemory
>
> Now I'm beginning to understand the problem with your 'fallback'
> function. If I specialize the capability to Handles to Memory as in:
>
> -- from regional-pointers:
> import Foreign.Ptr.Region ( Memory )
>
> fallback :: Resource (C.Handle (Memory a)) IO
>         -> Resource (C.Handle (Memory a)) IO
>         -> Resource (C.Handle (Memory a)) IO
> fallback (Resource primary) (Resource backup) = Resource l
>    where
>      l c = primary c `catch` (\(SomeException _) -> backup c)
>
> then the only way to create a value of type 'Resource (C.Handle
> (Memory a))' is to apply my previously posted 'resource' function to
> 'Memory nrOfBytes'. There's no other way to create Handles to Memory.
> Is that your problem?
>
> The problem is that Handle is an associated _data_ type not an
> associated _type synonym_:
>
> class Resource resource where
>    data Handle resource ∷ *
>    open ∷ resource → IO (Handle resource)
>    close ∷ Handle resource → IO ()
>
> So 'Handle' is an injective type function which means that every
> 'Handle resource' has only one 'resource'.
>
> I could try turning 'Handle' into an associated type synonym.
>
> Regards,
>
> Bas
>


More information about the Haskell-Cafe mailing list