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).