Bracket pattern

From HaskellWiki
Jump to: navigation, search

When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed in between.

The function bracket from Control.Exception can be used to build such functions in the IO monad. It is commonly partially applied to its first two parameters, giving a function which manages the resource in question, allocating it for the context of a specific action.

bracket :: IO a        -- computation to run first ("acquire resource")
        -> (a -> IO b) -- computation to run last ("release resource")
        -> (a -> IO c) -- computation to run in-between
        -> IO c

In general, throughout the libraries many functions having names beginning with with are defined to manage various resources in this fashion. One passes in a function of the allocated resource that says what to do, and the with-function handles both the allocation and deallocation. For example, in System.IO, there is a function:

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r

that manages opening and closing a filehandle, or in Foreign.Marshal.Array, there is a function

withArray :: Storable a => [a] -> (Ptr a -> IO b) -> IO b

which manages the memory associated with a foreign array.

A common thing one might want to do is to collect up a list of such resource-managing with-functions and build from them a single with-function that manages the whole list of associated resources.

For this one can use what is effectively sequence in the Cont monad, "unwrapped" appropriately:

nest :: [(r -> a) -> a] -> ([r] -> a) -> a
nest xs = runCont (sequence (map Cont xs))

As an example of the use of nest, here is a with-function that allocates each of a list of Strings as a CString (which as the name suggests, is a C-style pointer to an allocated block of memory holding the characters of the string), and then allocates a null-terminated array holding these CStrings, runs the given action, then deallocates all the resources.

withCStringArray0 :: [String] -> (Ptr CString -> IO a) -> IO a
withCStringArray0 strings act = nest (map withCString strings)
                                     (\rs -> withArray0 nullPtr rs act)

See also