diff --git a/Control/Parallel/Strategies.hs b/Control/Parallel/Strategies.hs index 9b2e6df..20bf8b0 100644 --- a/Control/Parallel/Strategies.hs +++ b/Control/Parallel/Strategies.hs @@ -628,68 +628,33 @@ parFmap strat f = (`using` parTraversable strat) . fmap f -- -------------------------------------------------------------------------- -- Strategies for lazy lists --- List-based non-compositional rolling buffer strategy, evaluating list --- elements to weak head normal form. --- Not to be exported; used in evalBuffer and for optimisation. -evalBufferWHNF :: Int -> Strategy [a] -evalBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0)) - where -- ret :: [a] -> [a] -> [a] - ret (x:xs) (y:ys) = y `pseq` (x : ret xs ys) - ret xs _ = xs - - -- start :: Int -> [a] -> [a] - start 0 ys = ys - start !_n [] = [] - start !n (y:ys) = y `pseq` start (n-1) ys - --- | 'evalBuffer' is a rolling buffer strategy combinator for (lazy) lists. --- Evaluation of the ith element induces evaluation of the (i+n)th element, --- with the first n elements being evaluated immediately. --- --- '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 --- +-- | 'evalBuffer' is a rolling buffer strategy combinator for lazy lists. +-- Pattern matching on the result of @evalBuffer n strat xs@ will evaluate the +-- first @n+1@ elements of @xs@ using @strat@. Pattern matching on each +-- additional list cons will evaluate an additional element using @strat@. evalBuffer :: Int -> Strategy a -> Strategy [a] -evalBuffer n strat = evalBufferWHNF n . map (withStrategy strat) - --- Like evalBufferWHNF, but sparks the list elements when pushing them --- into the buffer. --- Not to be exported; used in parBuffer and for optimisation. -parBufferWHNF :: Int -> Strategy [a] -parBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0)) - where -- ret :: [a] -> [a] -> [a] - ret (x:xs) (y:ys) = y `par` (x : ret xs ys) - ret xs _ = xs - - -- start :: Int -> [a] -> [a] - start 0 ys = ys - start !_n [] = [] - start !n (y:ys) = y `par` start (n-1) ys - - --- | Like 'evalBuffer', but evaluates the list elements in parallel when --- pushing them into the buffer. --- Evaluation of the ith element induces parallel evaluation of the (i+n)th element, --- with the first n elements being evaluated in parallel immediately. --- --- > parBuffer n strat = evalBuffer n (rparWith strat) +evalBuffer n0 strat xs0 = return (ret tied (drop n0 tied)) + where + -- This is the heart of the strategy. The idea is to tie the evaluation + -- of each cons (to WHNF) to the evaluation of its contents (according + -- to strat). Walking the spine of the result will thus perform + -- the requested Eval actions. + tied = foldr go [] xs0 + where + go x r = runEval ((: r) <$> strat x) + + ret (x : xs) (_y : ys) = x : ret xs ys + ret xs _ = xs + +-- | 'parBuffer' is a rolling buffer strategy combinator for lazy lists. +-- Pattern matching on the result of @parBuffer n s xs@ sparks +-- computations to evaluate the first @n+1@ elements of @xs@ using the +-- strategy @s@. Pattern matching on each additional list cons will +-- spark an additional computation. +-- +-- @parBuffer n strat = 'evalBuffer' n ('rparWith' strat)@ parBuffer :: Int -> Strategy a -> Strategy [a] -parBuffer n strat = parBufferWHNF n . map (withStrategy strat) --- Alternative definition via evalBuffer (may compromise firing of RULES): --- parBuffer n strat = evalBuffer n (rparWith strat) - --- Deforest the intermediate list in parBuffer/evalBuffer when it is --- unnecessary: - -{-# NOINLINE [1] evalBuffer #-} -{-# NOINLINE [1] parBuffer #-} -{-# RULES -"evalBuffer/rseq" forall n . evalBuffer n rseq = evalBufferWHNF n -"parBuffer/rseq" forall n . parBuffer n rseq = parBufferWHNF n - #-} +parBuffer n strat = evalBuffer n (rparWith strat) -- -------------------------------------------------------------------------- -- Strategies for tuples