HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Moduleparallel-3.2.2.0Haskell2010

Control.Parallel.Strategies

Parallel Evaluation Strategies, or Strategies for short, provide ways to express parallel computations. Strategies have the following key features:

  • Strategies express deterministic parallelism: the result of the program is unaffected by evaluating in parallel. The parallel tasks evaluated by a Strategy may have no side effects. For non-deterministic parallel programming, see Control.Concurrent.

  • Strategies let you separate the description of the parallelism from the logic of your program, enabling modular parallelism. The basic idea is to build a lazy data structure representing the computation, and then write a Strategy that describes how to traverse the data structure and evaluate components of it sequentially or in parallel.

  • Strategies are compositional: larger strategies can be built by gluing together smaller ones.

  • Monad and Applicative instances are provided, for quickly building strategies that involve traversing structures in a regular way.

For API history and changes in this release, see Control.Parallel.Strategies#history.

  • 4 types
  • 1 class
  • 63 values
  • Packageparallel-3.2.2.0
  • Exports68
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceStrategies.hs

The strategy type

1 declaration
typetype Strategy a = a -> Eval a
#

A Strategy is a function that embodies a parallel evaluation strategy. The function traverses (parts of) its argument, evaluating subexpressions in parallel or in sequence.

A Strategy may do an arbitrary amount of evaluation of its argument, but should not return a value different from the one it was passed.

Parallel computations may be discarded by the runtime system if the program no longer requires their result, which is why a Strategy function returns a new value equivalent to the old value. The intention is that the program applies the Strategy to a structure, and then uses the returned value, discarding the old value. This idiom is expressed by the using function.

Application of strategies

4 declarations

Composition of strategies

1 declaration
valuedot :: Strategy a -> Strategy a -> Strategy a
#

Compose two strategies sequentially. This is the analogue to function composition on strategies.

For any strategies strat1, strat2, and strat3,

(strat1 `dot` strat2) `dot` strat3 == strat1 `dot` (strat2 `dot` strat3)
strat1 `dot` strat1 = strat1
strat1 `dot` r0 == strat1
strat2 `dot` strat1 == strat2 . withStrategy strat1

Basic strategies

5 declarations
valuer0 :: Strategy a
#

r0 performs *no* evaluation.

r0 == evalSeq Control.Seq.r0
valuerseq :: Strategy a
#

rseq evaluates its argument to weak head normal form.

rseq == evalSeq Control.Seq.rseq
valuerparWith :: Strategy a -> Strategy a
#

Perform a computation in parallel using a strategy.

rparWith strat x

will spark strat x. Note that rparWith strat is not the same as rpar dot strat. Specifically, rpar dot strat always sparks a computation to reduce the result of the strategic computation to WHNF, while rparWith strat need not.

rparWith r0 = r0
rparWith rpar = rpar
rparWith rseq = rpar

rparWith rpar x creates a spark that immediately creates another spark to evaluate x. We consider this equivalent to rpar because there isn't any real additional parallelism. However, it is always less efficient because there's a bit of extra work to create the first (useless) spark. Similarly, rparWith r0 creates a spark that does precisely nothing. No real parallelism is added, but there is a bit of extra work to do nothing.

Injection of sequential strategies

2 declarations
valueevalSeq :: SeqStrategy a -> Strategy a
#

Inject a sequential strategy (ie. coerce a sequential strategy to a general strategy).

Thanks to evalSeq, the type Control.Seq.Strategy a is a subtype of Strategy a.

Strategies for traversable data types

2 declarations

Strategies for lists

10 declarations
valueevalListNth :: Int -> Strategy a -> Strategy [a]
#

Evaluate the nth element of a list (if there is such) according to the given strategy. This nth is 0-based. For example, [1, 2, 3, 4, 5] using evalListNth 4 rseq will eval 5, not 4. The spine of the list up to the nth element is evaluated as a side effect.

valueevalListSplitAt :: Int -> Strategy [a] -> Strategy [a] -> Strategy [a]
#

evaListSplitAt n stratPref stratSuff evaluates the prefix (of length n) of a list according to stratPref and its the suffix according to stratSuff.

valueparListChunk :: Int -> Strategy a -> Strategy [a]
#

Divides a list into chunks, and applies the strategy evalList strat to each chunk in parallel.

It is expected that this function will be replaced by a more generic clustering infrastructure in the future.

If the chunk size is 1 or less, parListChunk is equivalent to parList

valueparMap :: Strategy b -> (a -> b) -> [a] -> [b]
#

A combination of parList and map, encapsulating a common pattern:

parMap strat f = withStrategy (parList strat) . map f

Strategies for lazy lists

valueevalBuffer :: Int -> Strategy a -> Strategy [a]
#

evalBuffer is a rolling buffer strategy combinator for (lazy) lists.

evalBuffer is not as compositional as the type suggests. In fact, it evaluates list elements at least to weak head normal form, disregarding a strategy argument r0.

evalBuffer n r0 == evalBuffer n rseq

Strategies for tuples

16 declarations

Evaluate the components of a tuple according to the given strategies.

Evaluate the components of a tuple in parallel according to the given strategies.

Strategic function application

6 declarations
value($|) :: (a -> b) -> Strategy a -> a -> b
#

Sequential function application. The argument is evaluated using the given strategy before it is given to the function.

value($||) :: (a -> b) -> Strategy a -> a -> b
#

Parallel function application. The argument is evaluated using the given strategy, in parallel with the function application.

value(.|) :: (b -> c) -> Strategy b -> (a -> b) -> a -> c
#

Sequential function composition. The result of the second function is evaluated using the given strategy, and then given to the first function.

value(.||) :: (b -> c) -> Strategy b -> (a -> b) -> a -> c
#

Parallel function composition. The result of the second function is evaluated using the given strategy, in parallel with the application of the first function.

value(-|) :: (a -> b) -> Strategy b -> (b -> c) -> a -> c
#

Sequential inverse function composition, for those who read their programs from left to right. The result of the first function is evaluated using the given strategy, and then given to the second function.

value(-||) :: (a -> b) -> Strategy b -> (b -> c) -> a -> c
#

Parallel inverse function composition, for those who read their programs from left to right. The result of the first function is evaluated using the given strategy, in parallel with the application of the second function.

For Strategy programmers

4 declarations
newtypenewtype Eval a
#

Eval is a Monad that makes it easier to define parallel strategies. It is a strict identity monad: that is, in

m >>= f

m is evaluated before the result is passed to f.

instance Monad Eval where
  return  = Done
  m >>= k = case m of
              Done x -> k x

If you wanted to construct a Strategy for a pair that sparked the first component in parallel and then evaluated the second component, you could write

myStrat :: Strategy (a,b)
myStrat (a,b) = do { a' <- rpar a; b' <- rseq b; return (a',b') }

Alternatively, you could write this more compactly using the Applicative style as

myStrat (a,b) = (,) <$> rpar a <*> rseq b
Instances4Monad, Functor, MonadFix, Applicative
  • Monad EvalDefined in parallel-3.2.2.0 · Control.Parallel.Strategies
  • Functor EvalDefined in parallel-3.2.2.0 · Control.Parallel.Strategies
  • MonadFix EvalDefined in parallel-3.2.2.0 · Control.Parallel.Strategies
  • Applicative EvalDefined in parallel-3.2.2.0 · Control.Parallel.Strategies
valueparEval :: Eval a -> Eval a
#

parEval sparks the computation of its argument for evaluation in parallel. Unlike rpar . runEval, parEval

  • does not exit the Eval monad

  • does not have a built-in rseq, so for example parEval (r0 x) behaves as you might expect (it creates a spark that does no evaluation).

It is related to rparWith by the following equality:

parEval . strat = rparWith strat
valuerunEval :: Eval a -> a
#

Pull the result out of the monad.

valuerunEvalIO :: Eval a -> IO a
#

Run the evaluation in the IO monad. This allows sequencing of evaluations relative to IO actions.

API History

0 declarations

The strategies library has a long history. What follows is a summary of how the current design evolved, and is mostly of interest to those who are familiar with an older version, or need to adapt old code to use the newer API.

Version 1.x

The original Strategies design is described in Algorithm + Strategy = Parallelism http://www.macs.hw.ac.uk/~dsg/gph/papers/html/Strategies/strategies.html and the code was written by Phil Trinder, Hans-Wolfgang Loidl, Kevin Hammond et al.

Version 2.x

Later, during work on the shared-memory implementation of parallelism in GHC, we discovered that the original formulation of Strategies had some problems, in particular it lead to space leaks and difficulties expressing speculative parallelism. Details are in the paper Runtime Support for Multicore Haskell http://community.haskell.org/~simonmar/papers/multicore-ghc.pdf.

This module has been rewritten in version 2. The main change is to the 'Strategy a' type synonym, which was previously a -> Done and is now a -> Eval a. This change helps to fix the space leak described in "Runtime Support for Multicore Haskell". The problem is that the runtime will currently retain the memory referenced by all sparks, until they are evaluated. Hence, we must arrange to evaluate all the sparks eventually, just in case they aren't evaluated in parallel, so that they don't cause a space leak. This is why we must return a "new" value after applying a Strategy, so that the application can evaluate each spark created by the Strategy.

The simple rule is this: you must use the result of applying a Strategy if the strategy creates parallel sparks, and you should probably discard the the original value. If you don't do this, currently it may result in a space leak. In the future (GHC 6.14), it will probably result in lost parallelism instead, as we plan to change GHC so that unreferenced sparks are discarded rather than retained (we can't make this change until most code is switched over to this new version of Strategies, because code using the old verison of Strategies would be broken by the change in policy).

The other changes in version 2.x are:

  • Strategies can now be defined using a convenient Monad/Applicative type, Eval. e.g. parList s = traverse (Par . (`using` s))

  • parList has been generalised to parTraverse, which works on any Traversable type, and similarly seqList has been generalised to seqTraverse

  • parList and parBuffer have versions specialised to rwhnf, and there are transformation rules that automatically translate e.g. parList rwnhf into a call to the optimised version.

  • NFData has been moved to Control.DeepSeq in the deepseq package. Note that since the Strategy type changed, rnf is no longer a Strategy: use rdeepseq instead.

Version 2.1 moved NFData into a separate package, deepseq.

Version 2.2 changed the type of Strategy to a -> Eval a, and re-introduced the r0 strategy which was missing in version 2.1.

Version 2.3 simplified the Eval type, so that Eval is now just the strict identity monad. This change and various other improvements and refactorings are thanks to Patrick Maier who noticed that Eval didn't satisfy the monad laws, and that a simpler version would fix that problem.

(version 2.3 was not released on Hackage).

Version 3 introduced a major overhaul of the API, to match what is presented in the paper

Seq no More: Better Strategies for Parallel Haskell http://community.haskell.org/~simonmar/papers/strategies.pdf

The major differences in the API are:

The naming scheme is now as follows:

  • Basic polymorphic strategies (of type Strategy a) are called r.... Examples: r0, rseq, rpar, rdeepseq.

  • A strategy combinator for a particular type constructor or constructor class T is called evalT..., parT... or seqT....

  • The seqT... combinators (residing in module Control.Seq) yield sequential strategies. Thus, seqT... combinators cannot spark, nor can the sequential strategies to which they may be applied. Examples: seqTuple2, seqListN, seqFoldable.

  • The evalT... combinators do not spark themselves, yet they may be applied to strategies that do spark. (They may also be applied to non-sparking strategies; however, in that case the corresponding seqT... combinator might be a better choice.) Examples: evalTuple2, evalListN, evalTraversable.

  • The parT... combinators, which are derived from their evalT... counterparts, do spark. They may be applied to all strategies, whether sparking or not. Examples: parTuple2, parListN, parTraversable.

  • An exception to the type driven naming scheme are evalBuffer and parBuffer, which are not named after their type constructor (lists) but after their function (rolling buffer of fixed size).

Backwards compatibility

14 declarations

These functions and types are all deprecated, and will be removed in a future release. In all cases they have been either renamed or replaced with equivalent functionality.

typetype Done = ()
#

Deprecated. The Strategy type is now a -> Eval a, not a -> Done

DEPRECCATED: replaced by the Eval monad

valuedemanding :: a -> Done -> a
#

Deprecated. Use pseq or $| instead

DEPRECATED: Use pseq or $| instead

valuesparking :: a -> Done -> a
#

Deprecated. Use par or $|| instead

DEPRECATED: Use par or $|| instead

value(>|) :: Done -> Done -> Done
#

Deprecated. Use pseq or $| instead

DEPRECATED: Use pseq or $| instead

valueunEval :: Eval a -> a
#

Deprecated. renamed to runEval

DEPRECATED: renamed to runEval

For API completeness

1 declaration

so users of rdeepseq aren't required to import Control.DeepSeq:

classclass NFData a where
#

A class of types that can be fully evaluated.

Instances124NFData, …