caf (empty) → 0.0.1
raw patch · 11 files changed
+735/−0 lines, 11 filesdep +basesetup-changed
Dependencies added: base
Files
- Control/Concurrent/Futures.hs +80/−0
- Control/Concurrent/Futures/BChan.hs +50/−0
- Control/Concurrent/Futures/Buffer.hs +101/−0
- Control/Concurrent/Futures/Chan.hs +106/−0
- Control/Concurrent/Futures/Futures.hs +155/−0
- Control/Concurrent/Futures/HQSem.hs +67/−0
- Control/Concurrent/Futures/QSem.hs +86/−0
- LICENSE +27/−0
- README +35/−0
- Setup.hs +4/−0
- caf.cabal +24/−0
+ Control/Concurrent/Futures.hs view
@@ -0,0 +1,80 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This package implements various kinds of concurrency abstractions using futures.+Maintainer : mwillig@gmx.de+Stability : experimental+Portability : portable++This package implements futures and various kinds of concurrency abstractions +using futures.++Threads can synchronise their values via futures. +Future values are lazily evaluated so they explicitly suspend the computation. +Each future object is associated with a background thread that computes the future value. +As long as this expression has not been evaluated, the value of the future is unknown. +Whenever an unknown future is accessed the computation will suspend on this future. +Once the value has been evaluated the computation resumes. A handle is a component +that points to an unevaluated future and computes its value on demand.+Therefore, handles are used to associate a value to a future. They provide+a synchronisation mechanism for processes.++Example+This example shows how you can use "Buffer" to concurrently compute the values of +nodes from a binary tree.++> data BTree a = BLeaf a | BNode a (BTree a) (BTree a)++> concSumB :: (Num a) => BTree a -> IO a+> concSumB t = do +> result <- newBuf+> case t of+> BLeaf a -> putBuf result a;+> BNode a t1 t2 -> sumB result t +> out <- getBuf result+> return out++> sumB :: (Num a) => Buffer a -> BTree a -> IO ()+> sumB mvar tree = do +> case tree of +> BLeaf a -> putBuf mvar a +> BNode a t1 t2 -> do+> sem <- newBuf+> forkIO (sumB sem t1)+> forkIO (sumB sem t2)+> erg1 <-getBuf sem+> erg2 <-getBuf sem +> putBuf mvar (erg1 + erg2 + a)++You can test the function with the following test data++> bintree = BNode 1 (BNode 24 (BLeaf 2) (BNode 6 (BLeaf 24) (BLeaf 3)))(BNode 33 (BLeaf 7) (BLeaf 8))+> concSumB bintree++-}+module Control.Concurrent.Futures (+ module Control.Concurrent.Futures.Futures,+ module Control.Concurrent.Futures.Buffer,+ module Control.Concurrent.Futures.Chan,+ module Control.Concurrent.Futures.QSem,+ module Control.Concurrent.Futures.HQSem,+ module Control.Concurrent.Futures.BChan,+ module Control.Concurrent.Futures.Barrier,+ module Control.Concurrent.Futures.Examples,+ ) where++import Control.Concurrent.Futures.Futures+import Control.Concurrent.Futures.Buffer+import Control.Concurrent.Futures.Chan+import Control.Concurrent.Futures.QSem+import Control.Concurrent.Futures.HQSem+import Control.Concurrent.Futures.BChan+import Control.Concurrent.Futures.Barrier+import Control.Concurrent.Futures.Examples+++-- internal function+--wait :: Bool -> IO Bool+--wait x = do+-- case x of+-- True -> return x+-- otherwise -> return x
+ Control/Concurrent/Futures/BChan.hs view
@@ -0,0 +1,50 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This module implements a bounded channel concurrency primitive using channels and quantity semaphores+Maintainer : mwillig@gmx.de+Stability : experimental+Portability : non-portable (requires Futures)++This modules combines a quantity semaphore from the module Control.Concurrent.Fututes.QSem and a channel from +module Control.Concurrent.Fututes.Chan to a new synchronisation primitive. Bounded channels have a limited +capacity of storage cells.+Warning: All operations on bounded channels should only be used within the +global wrapper function 'Futures.withFuturesDo'!+-}+module Control.Concurrent.Futures.BChan (+ BChan,+ newBChan,+ readBChan,+ writeBChan++) where+import Control.Concurrent.Futures.Chan+import Control.Concurrent.Futures.QSem++--data BChan a = BChan a (Chan a, QSem )+--type BChanType a = (ChanType a, QSem)++type BChan a = (Chan a, QSem)++-- | Creates a new bounded channel+newBChan :: Int -> IO (BChan a)+newBChan n = do+ chan <- newChan+ qsem <- newQSem n+ return (chan , qsem)++-- | Performs an up-operation on the QSem of the bounded channel and then reads+-- a value from the channel. The read operation may block.+readBChan :: BChan a -> IO a+readBChan (chan, sem) = do+ up sem+ readChan chan++-- | Performs a down-operations on the QSem of the bounded channel and writes a+-- new value to it. The down-operation may block.+writeBChan :: BChan a -> a -> IO () +writeBChan (chan, sem) val = do+ down sem+ writeChan chan val+ +
+ Control/Concurrent/Futures/Buffer.hs view
@@ -0,0 +1,101 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This module implements a buffer with cells and futures.+Maintainer : mwillig@gmx.de+Stability : experimental+Portability : non-portable (requires Futures)++This module implements one-place buffers using futures.+Warning: All operations on buffers should only be used within the global wrapper function+'Futures.withFuturesDo'!+-}+module Control.Concurrent.Futures.Buffer ( + Buffer,+-- Cell,+-- cell,+-- testAndSet,+ wait,+ newBuf,+ putBuf,+ getBuf+) where++import Control.Concurrent.Futures.Futures as Futures+import Control.Concurrent.MVar+import System.IO++-- -- The buffer type contains of 3 cells and a handle.+type Buffer a = (Cell Bool, Cell Bool, Cell a, Cell (Bool -> IO ()))++-----------------------------------------------------------------------+---Cells++-- | A cell type. Cells provide an automic 'exchange' operation.+type Cell a = MVar a++-- | Creates a new cell.+cell :: a -> IO (Cell a)+cell a = newMVar a++exchange :: Cell a -> a -> IO a +exchange a b = swapMVar a b++-- | TestAndSet on cells provides test and set functions in one atomic operation.+testAndSet :: Cell Bool -> IO t -> IO Bool+testAndSet cell code = do + val <- (exchange cell True)+ case val of+ True -> return True+ False -> do + code+ exchange cell False++-- | A test on cells+--tsExample = do+-- c <- Buffer.cell False+-- code <- (\x -> do putStrLn "The code." return x)+-- Buffer.testAndSet c code+-- return c+-------------------------------------------------------------------------------++-- | Waits its argument to become true+wait :: Bool -> IO Bool+wait x = do+ case x of+ True -> return x+ otherwise -> return x++-- | Creates a new empty buffer.+newBuf :: IO (Buffer a)+newBuf = do+ (h,f) <- Futures.newhandled+ (h',f') <- Futures.newhandled+ putg <- cell True+ getg <- cell f+ stored <- cell f'+ handler <- cell h+ return (putg,getg,stored,handler)++-- | Puts a new value to a buffer. 'putBuf' blocks if+-- the buffer is full. +putBuf :: Buffer a -> a -> IO ()+putBuf (putg,getg,stored,handler) val = do+ (h,f) <- Futures.newhandled+ old_value <- exchange putg f+ wait old_value+ exchange stored val+ old_handler <- exchange handler h+ old_handler True++-- | Gets the contents of a non-empty buffer. If the buffer is empty, then +-- this function blocks until the buffer is filled.+getBuf :: Buffer a -> IO a +getBuf (putg,getg,stored,handler) = do+ (h,f) <- Futures.newhandled+ (h',f') <- Futures.newhandled+ old_value <- exchange getg f+ wait old_value+ val <- exchange stored f'+ old_handler <- exchange handler h+ old_handler True+ return val
+ Control/Concurrent/Futures/Chan.hs view
@@ -0,0 +1,106 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This module implements a channel concurrency primitive using Buffers.+Maintainer : mwillig@gmx.de+Stability : experimental+Portability : non-portable (requires Futures)++This module implements a channel synchronisation primitive using buffers that block on+futures. A channel is a linked list of buffers. It has a read-end at one side and a+write-end at the other. Elements put into the channel can be read out in+a first in, first out order. A read and a write operation can be executed in+parallel by several threads. A channel has no capacity bounding.++The module contains similar functions as 'Control.Concurrent.Futures.Chan'.++Warning: All operations on channels should only be used within the global wrapper function+'Futures.withFuturesDo'!+-}+module Control.Concurrent.Futures.Chan (+ Chan,+ ChanType,+ newChan,+ writeChan,+ readChan,+ writeChanContents,+ getChanContents,+ mergeChan+ ) where+ +-- qualified, because channel functions are also defined in +-- Control.Concurrent+import qualified Control.Concurrent+import System.IO.Unsafe ( unsafeInterleaveIO )+import Control.Concurrent.Futures.Buffer+import Control.Concurrent.Futures.Futures++-- | A channel consists of a read-end buffer and a write-end buffer. +-- The Itemtype is required to link the buffers in the channel.+data Chan a+ = Chan (Buffer (ItemType a))+ (Buffer (ItemType a))++type ChanType a = ((Buffer (ItemType a)), (Buffer (ItemType a)))+type ItemType a = (Buffer(Item a)) +data Item a = Item a (ItemType a)++-- | Creates a new empty channel. +newChan :: IO (Chan a)+newChan = do+ hole <- newBuf+ read_end <- newBuf+ write_end <- newBuf+ putBuf read_end hole+ putBuf write_end hole+ return (Chan read_end write_end)+ +-- | Writes one value to a channel. A 'writeChan' never blocks, since channels have +-- no bounding limiters.+writeChan :: Chan a -> a -> IO ()+writeChan (Chan read_end write_end) val = do+ new_hole <- newBuf+ old_hole <- getBuf write_end+ putBuf write_end new_hole+ putBuf old_hole (Item val new_hole)++ +-- | Reads out an item from the read-head of the channel.+-- It blocks on a empty channel.+readChan :: Chan a -> IO a+readChan (Chan read_end write_end) = do+ chan_head <- getBuf read_end+ (Item val content) <- getBuf chan_head+ putBuf read_end content+ return val+++-- | Implements the same behaviour as writeChanContents from the module Control.Concurrent.Chan.+writeChanContents :: Chan a -> [a] -> IO ()+writeChanContents chan (x:xs) = do+ Control.Concurrent.forkIO (writeChan chan x)+ writeChanContents chan xs+ >>= return+writeChanContents chan [] = return ()++-- | Implements the same behaviour as getChanContents from the module Control.Concurrent.Chan.+-- It reads permanently from the channel.+getChanContents :: Chan a -> IO [a]+getChanContents ch+ = unsafeInterleaveIO ( do+ x <- readChan ch+ xs <- getChanContents ch+ return (x:xs)+ )++-- | Writes two equally typed lists to a given channel in parallel.+mergeChan :: [a] -> [a] -> Chan a -> IO (Chan a)+mergeChan l1 l2 cm = do+ Control.Concurrent.forkIO (merge l1 cm)+ Control.Concurrent.forkIO (merge l2 cm)+ return cm++-- internal function +merge (x:xs) c = do+ Control.Concurrent.forkIO (writeChan c x)+ (merge xs c)+merge [] c = return ()
+ Control/Concurrent/Futures/Futures.hs view
@@ -0,0 +1,155 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This module implements several kinds of futures using Concurrent Haskell+Maintainer : sabel@ki.cs.uni-frankfurt.de+Stability : provisional+Portability : portable++This module implements explicit futures ('EFuture', 'efuture', 'force') as well as several variants of implicit futures+('future', 'recursiveFuture', 'strictFuture', 'strictRecursiveFuture', 'lazyFuture', 'lazyRecursiveFuture')+While explicit futures must be forced (using 'force') if their value is needed, this is not necessary for implicit futures.+For implicit futures it is necessary to put them into the global wrapper 'withFuturesDo'.+-}++module Control.Concurrent.Futures.Futures (+ EFuture,+ efuture,+ force,+ future,+ recursiveFuture,+ withFuturesDo,+ strictFuture,+ strictRecursiveFuture,+ lazyFuture,+ lazyRecursiveFuture,+ hbind,+ newhandled,+ bhandle+ ) where+import Control.Concurrent+import Control.Exception(evaluate)+import System.IO.Unsafe++--import Data.IO++-- | The type 'EFuture' implements explicit futures, i.e. if the value of the future is need it must be forced explicitly using 'force'+type EFuture a = MVar a++-- | 'efuture' creates an explicit future, i.e. the computation is performed concurrently. The future value can be forced using 'force'+efuture :: IO a -> IO (EFuture a)+efuture act = + do ack <- newEmptyMVar+ forkIO (act >>= putMVar ack)+ return ack++-- | 'force' forces the value of an explicit future ('EFuture'), i.e. the calling thread blocks until the result becomes available.+force :: EFuture a -> IO a+force = takeMVar+++-- | 'future' creates an implicit future. A non-blocking concurrent computation is started. If the value of the future is needed, then+-- the future will be forced implicitly. The concurrent computation is killed if the calling thread stops, even if 'future' is used+-- within 'withFuturesDo'.+future :: IO a -> IO a +future code = do ack <-newEmptyMVar+ thread <- forkIO (code >>= putMVar ack)+ unsafeInterleaveIO (do result <- takeMVar ack+ killThread thread+ return result)++-- | 'recursiveFuture' behaves similar to 'future' with the difference that the future is recursive, i.e. the future created by+-- 'recursiveFuture' is used as argument of the code of the future.+recursiveFuture :: (a -> IO a) -> IO a +recursiveFuture code = do ack <- newEmptyMVar+ res <- unsafeInterleaveIO (takeMVar ack)+ thread <- forkIO (code res >>= putMVar ack)+ unsafeInterleaveIO (do res' <- evaluate res+ killThread thread+ return res')++-- --------------------------------------------------+-- not-exported functions for the global manager, they shouldn't be visible outside this module.+-- ++-- The manager is an MVar containing a list of unit-tuples+type Manager = MVar [()]++-- creating a new Manager+newManager :: IO Manager+newManager = newMVar []++-- register a future to a manager+register :: a -> Manager -> IO ()+register l man = do+ list <- takeMVar man+ putMVar man ((seq l ()):list)++-- synchronizeMan forces the evaluation of all registered futures +synchronizeMan :: Manager -> IO ()+synchronizeMan man = do + list <- takeMVar man+ seqList list++seqList [] = return ()+seqList (x:xs) = seq x (seqList xs)+++globalMan = unsafePerformIO newManager++--+-- --------------------------------------------------++-- | 'withFuturesDo' is the global wrapper which should be used around the code involving futures.+-- I.e., instead of writing @main=code@ one should use @main=withFuturesDo code@. Note, that there+-- should be only one call to 'withFuturesDo' in a program. +withFuturesDo :: IO () -> IO ()+withFuturesDo code = do code + synchronizeMan globalMan+++-- | creating a strict future is similar to 'future' with the difference that if used inside 'withFuturesDo'+-- it is guaranteed that the concurrent computation is forced (and finished) before the main thread terminates.+-- Warning: 'strictFuture' should only be used within the global wrapper 'withFuturesDo'!+strictFuture :: IO a -> IO a+strictFuture code = do fut <- future code+ register fut globalMan+ return fut++-- | a recursive variant of 'strictFuture' (see 'recursiveFuture' and 'future)+-- Warning: 'strictRecursiveFuture' should only be used within the global wrapper 'withFuturesDo'!+strictRecursiveFuture :: (a -> IO a) -> IO a +strictRecursiveFuture code = do fut <- recursiveFuture code+ register fut globalMan+ return fut++-- | a lazy future. Initially, no concurrent computation is started, but if the lazy future gets (implicitly) forced,+-- then the lazy future becomes a strict future.+-- Warning: 'lazyFuture' should only be used within the global wrapper 'withFuturesDo'!+lazyFuture :: IO a -> IO a +lazyFuture code = unsafeInterleaveIO (strictFuture code)++-- | a recursive variant of 'lazyFuture' (see 'recursiveFuture' and 'future)+-- Warning: 'lazyRecursiveFuture' should only be used within the global wrapper 'withFuturesDo'!+lazyRecursiveFuture :: (a -> IO a) -> IO a +lazyRecursiveFuture code = unsafeInterleaveIO (strictRecursiveFuture code)++-- | a new handle component. +bhandle :: (a -> (a -> IO ()) -> t) -> IO t+bhandle x = do + f' <- newEmptyMVar+ f <- lazyFuture (do + v <- takeMVar f'+ putMVar f' v+ return v+ )+ h <- strictFuture (return (\z -> (putMVar f' z)))+ return (x f h)+ +-- | creates a new handle.+newhandled :: IO (a -> IO (), a)+newhandled = bhandle (\f -> \h -> (h,f))+++-- | binds a handle to its value.+hbind :: (t -> t1) -> t -> t1+hbind h v = h v
+ Control/Concurrent/Futures/HQSem.hs view
@@ -0,0 +1,67 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This module implements a quantity semaphores with handles+Maintainer : mwillig@gmx.de+Stability : experimental+Portability : non-portable (requires Futures)++This module implements a quantity semaphore using handles that block on+futures.+A HQSem equals to QSemN in Control.Concurrent.+A Buffer euqals to QSem in Control.Concurrent.+Warning: All operations on quantity semaphores should only be used within the +global wrapper function 'Futures.withFuturesDo'!+-}+module Control.Concurrent.Futures.HQSem (+ HQSem,+ newHQSem,+ upHQSem,+ downHQSem+) where++import qualified Control.Concurrent+import System.IO.Unsafe ( unsafeInterleaveIO )+import Control.Concurrent.Futures.Futures as Futures+import Control.Concurrent.Futures.Buffer++-- | A handled quantity semaphores contains of a capacity and a waiting queue containing +-- handles.+type HQSem = Buffer (Int, [Bool -> IO ()])++-- | Creates a new quantity semaphore of capacity cnt.+newHQSem :: Int -> IO (HQSem)+newHQSem cnt = do+ b <- newBuf+ putBuf b (cnt,[])+ return b+ ++-- | Increments the semaphore's value, if there are no waiters.+-- 'up' reads out of the waiting queue and binds a waiting handle to True.+-- Note: This operation equals to signalQSemN in Control.Concurrent.QSemN.+upHQSem :: HQSem -> IO ()+upHQSem qsem = do+ b <- getBuf qsem+ case b of+ (cnt,ls) -> case ls of+ [] -> do putBuf qsem (cnt+1,ls)+ x:xs -> do+ x True+ putBuf qsem (cnt,ls)++-- | Decrements the semaphore's value. If the value has already reached 0, then +-- 'down' creates a new handle that is being added to the semaphore's waiting queue.+-- It blocks until the handle assigns a value to its future by a 'up'.+-- Note: This operation equals to waitQSemN in Control.Concurrent.QSemN.+downHQSem :: HQSem -> IO (Bool)+downHQSem qsem = do+ b <-getBuf qsem+ case b of+ (cnt,ls) -> case (cnt==0) of+ True -> do+ (h,f) <- Futures.newhandled+ putBuf qsem (cnt,h:ls)+ (wait f)+ False -> do+ putBuf qsem (cnt-1,ls)+ return True
+ Control/Concurrent/Futures/QSem.hs view
@@ -0,0 +1,86 @@+{- |+Module : <File name or $Header$ to be replaced automatically>+Description : This module implements a quantity semaphores with buffers+Maintainer : mwillig@gmx.de+Stability : experimental+Portability : non-portable (requires Futures)++This module implements a quantity semaphore using buffers that block on+futures.++A QSem equals to QSemN in Control.Concurrent.+A Buffer equals to QSem in Control.Concurrent.++Warning: All operations on quantity semaphores should only be used within the +global wrapper function 'Futures.withFuturesDo'!+-}+module Control.Concurrent.Futures.QSem (+ QSem,+ newQSem,+ up,+ down,+ enter+) where++import qualified Control.Concurrent+import System.IO.Unsafe ( unsafeInterleaveIO )+import Control.Concurrent.Futures.Futures+import Control.Concurrent.Futures.Buffer++-- | A quantity semaphores contains of a capacity and a waiting queue containing +-- buffers.+type QSem = Buffer (Int, [Buffer Bool])++-- | Creates a new quantity semaphore of capacity cnt.+newQSem :: Int -> IO (Buffer (Int, [Buffer Bool]))+newQSem cnt = do+ b <- newBuf+ putBuf b (cnt,[])+ return b+ -- For comparison the implementation of the Concurrent Haskell Library +--newQSem :: Int -> IO QSem+--newQSem init = do+--sem <- newMVar (init,[])+--return (QSem sem)+++-- | Increments the semaphore's value, if there are no waiters.+-- 'up' reads out of the waiting queue and writes True into a waiting 'Buffer'.+-- Note: This operation equals to signalQSemN in Control.Concurrent.QSemN.+up :: QSem -> IO ()+up qsem = do+ (cnt,ls) <- getBuf qsem+ case ls of+ [] -> do putBuf qsem (cnt+1,ls)+ x:xs -> do+ putBuf x True+ putBuf qsem (cnt,ls)++-- | Decrements the semaphore's value. If the value has already reached zero, then +-- 'down' creates a new empty 'Buffer' that is being added to the semaphore's waiting queue.+-- It blocks until the buffer gets filled by a 'up'.+-- Note: This operation equals to waitQSemN in Control.Concurrent.QSemN.+down :: QSem -> IO Bool+down qsem = do+ b <-getBuf qsem+ case b of+ (cnt,ls) -> case (cnt==0) of+ True -> do+ b1 <- newBuf+ putBuf qsem (cnt,b1:ls)+ getBuf b1+ False -> do+ putBuf qsem (cnt-1,ls)+ return True++-- | Use the quantity semaphore to limit the computation of code. This function+-- performs a down on the given q. s., executues the code and returns after a up+-- on the q.s. .+enter :: QSem -> IO a -> IO ()+enter qsem code = do+ x <- down qsem+ case x of+ True -> do+ code + up qsem+ False -> return ()
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright 2009, Martina Willig <willig@ki.informatik.uni-frankfurt.de>++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ README view
@@ -0,0 +1,35 @@+Concurrency Abstractions with Futures+======================================+More info on the package, releases, etc. can be found at + + http://haskell.forkIO.com/dotnet+ +--------------------------------------+Install distribution package+--------------------------------------+1. Unzip the tarball+2. Navigate to the directory+3. Run the installation commands+ runhaskell Setup configure+ runhaskell Setup build+ sudo runhaskell Setup install++This last step will register thedistribution package.+Now in your Haskell programs, you can simply import the new modules from the distribution package.++--------------------------------------+Uninstall distribution package+--------------------------------------+See a list of installed packages with this command+ ghc-pkg list++Unregister the package with+ ghc-pkg unregister caf-x.x++--------------------------------------+Feedback+--------------------------------------+Please send Feedback to ++willig@ki.informatik.uni-frankfurt.de +
+ Setup.hs view
@@ -0,0 +1,4 @@+-- file: ch05/Setup.hs+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ caf.cabal view
@@ -0,0 +1,24 @@+Name: caf+Version: 0.0.1+Description: This library contains implementations of several kinds of futures and concurrency abstractions.+License: BSD3+License-file: LICENSE+Author: Dr. David Sabel and Martina Willig +Maintainer: Martina Willig <willig@ki.informatik.uni-frankfurt.de>+Build-Type: Simple+Stability: experimental+Synopsis: A library of Concurrency Abstractions using Futures.+Category: Concurrency+Cabal-Version: >= 1.2+Extra-Source-Files: README+library+ Exposed-Modules: Control.Concurrent.Futures+ Control.Concurrent.Futures.Futures+ Control.Concurrent.Futures.Buffer+ Control.Concurrent.Futures.Chan+ Control.Concurrent.Futures.QSem+ Control.Concurrent.Futures.BChan+ Control.Concurrent.Futures.HQSem+ Build-Depends: base >= 2.0++