Skip to content

Commit 9ea4c07

Browse files
committed
Make things simpler and more predictable
* `Eval` has always been a hand-written copy of `IO`. Use a newtype wrapper around `IO` instead. This gives us the necessary instances for free and shifts the proof obligations into `base`. * Use `unsafeDupablePerformIO` instead of applying `realWorld#` directly. This should make the optimizer much less likely to eat our shorts. * Redefine `rparWith` to do the simplest thing that could possibly work. It seems to do so. * Remove the rewrite rule for `parList`; as far as I can tell, it slows things down. Fixes #17
1 parent ab08048 commit 9ea4c07

File tree

1 file changed

+24
-48
lines changed

1 file changed

+24
-48
lines changed

Control/Parallel/Strategies.hs

Lines changed: 24 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE BangPatterns, CPP, MagicHash, UnboxedTuples #-}
2+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23
-----------------------------------------------------------------------------
34
-- |
45
-- Module : Control.Parallel.Strategies
@@ -145,11 +146,19 @@ import Control.Applicative
145146
#endif
146147
import Control.Parallel
147148
import Control.DeepSeq (NFData(rnf))
149+
150+
#if MIN_VERSION_base(4,4,0)
151+
import System.IO.Unsafe (unsafeDupablePerformIO)
152+
import Control.Exception (evaluate)
153+
#else
154+
import System.IO.Unsafe (unsafePerformIO)
148155
import Control.Monad
156+
#endif
149157

150158
import qualified Control.Seq
151159

152160
import GHC.Exts
161+
import GHC.IO (IO (..))
153162

154163
infixr 9 `dot` -- same as (.)
155164
infixl 0 `using` -- lowest precedence and associate to the left
@@ -192,27 +201,19 @@ infixl 0 `using` -- lowest precedence and associate to the left
192201

193202
#if __GLASGOW_HASKELL__ >= 702
194203

195-
newtype Eval a = Eval (State# RealWorld -> (# State# RealWorld, a #))
204+
newtype Eval a = Eval {unEval_ :: IO a}
205+
deriving (Functor, Applicative, Monad)
196206
-- GHC 7.2.1 added the seq# and spark# primitives, that we use in
197207
-- the Eval monad implementation in order to get the correct
198208
-- strictness behaviour.
199209

200210
-- | Pull the result out of the monad.
201211
runEval :: Eval a -> a
202-
runEval (Eval x) = case x realWorld# of (# _, a #) -> a
203-
204-
instance Functor Eval where
205-
fmap = liftM
206-
207-
instance Applicative Eval where
208-
pure x = Eval $ \s -> (# s, x #)
209-
(<*>) = ap
210-
211-
instance Monad Eval where
212-
return = pure
213-
Eval x >>= k = Eval $ \s -> case x s of
214-
(# s', a #) -> case k a of
215-
Eval f -> f s'
212+
# if MIN_VERSION_base(4,4,0)
213+
runEval = unsafeDupablePerformIO . unEval_
214+
# else
215+
runEval = unsafePerformIO . unEval_
216+
# endif
216217
#else
217218

218219
data Eval a = Done a
@@ -234,9 +235,6 @@ instance Monad Eval where
234235

235236
{-# RULES "lazy Done" forall x . lazy (Done x) = Done x #-}
236237

237-
#endif
238-
239-
240238
-- The Eval monad satisfies the monad laws.
241239
--
242240
-- (1) Left identity:
@@ -259,6 +257,8 @@ instance Monad Eval where
259257
-- ==> undefined <== undefined >>= (\x -> f x >>= g)
260258
-- <*= m >>= (\x -> f x >>= g)
261259

260+
#endif
261+
262262

263263
-- -----------------------------------------------------------------------------
264264
-- Strategies
@@ -356,10 +356,13 @@ r0 x = return x
356356
--
357357
rseq :: Strategy a
358358
#if __GLASGOW_HASKELL__ >= 702
359-
rseq x = Eval $ \s -> seq# x s
359+
rseq x = Eval (evaluate x)
360360
#else
361361
rseq x = x `seq` return x
362362
#endif
363+
-- Staged NOINLINE so we can match on rseq in RULES
364+
{-# NOINLINE [1] rseq #-}
365+
363366

364367
-- Proof of rseq == evalSeq Control.Seq.rseq
365368
--
@@ -388,7 +391,7 @@ rdeepseq x = do rseq (rnf x); return x
388391
-- | 'rpar' sparks its argument (for evaluation in parallel).
389392
rpar :: Strategy a
390393
#if __GLASGOW_HASKELL__ >= 702
391-
rpar x = Eval $ \s -> spark# x s
394+
rpar x = Eval $ IO $ \s -> spark# x s
392395
#else
393396
rpar x = case (par# x) of { _ -> Done x }
394397
#endif
@@ -406,12 +409,7 @@ rpar x = case (par# x) of { _ -> Done x }
406409
--
407410
rparWith :: Strategy a -> Strategy a
408411
#if __GLASGOW_HASKELL__ >= 702
409-
rparWith s a = do l <- rpar r; return (case l of Lift x -> x)
410-
where r = case s a of
411-
Eval f -> case f realWorld# of
412-
(# _, a' #) -> Lift a'
413-
414-
data Lift a = Lift a
412+
rparWith s = rpar `dot` s
415413
#else
416414
rparWith s a = do l <- rpar (s a); return (case l of Done x -> x)
417415
#endif
@@ -502,26 +500,6 @@ chunk :: Int -> [a] -> [[a]]
502500
chunk _ [] = []
503501
chunk n xs = as : chunk n bs where (as,bs) = splitAt n xs
504502

505-
-- Non-compositional version of 'parList', evaluating list elements
506-
-- to weak head normal form.
507-
-- Not to be exported; used for optimisation.
508-
509-
-- | DEPRECATED: use @'parList' 'rseq'@ instead
510-
parListWHNF :: Strategy [a]
511-
parListWHNF xs = go xs `pseq` return xs
512-
where -- go :: [a] -> [a]
513-
go [] = []
514-
go (y:ys) = y `par` go ys
515-
516-
-- The non-compositional 'parListWHNF' might be more efficient than its
517-
-- more compositional counterpart; use RULES to do the specialisation.
518-
519-
{-# NOINLINE [1] parList #-}
520-
{-# NOINLINE [1] rseq #-}
521-
{-# RULES
522-
"parList/rseq" parList rseq = parListWHNF
523-
#-}
524-
525503
-- --------------------------------------------------------------------------
526504
-- Convenience
527505

@@ -752,8 +730,6 @@ seqTraverse = evalTraversable
752730
parTraverse :: Traversable t => Strategy a -> Strategy (t a)
753731
parTraverse = parTraversable
754732

755-
{-# DEPRECATED parListWHNF "use (parList rseq) instead" #-}
756-
757733
{-# DEPRECATED seqList "renamed to evalList" #-}
758734
-- | DEPRECATED: renamed to 'evalList'
759735
seqList :: Strategy a -> Strategy [a]

0 commit comments

Comments
 (0)