To make a list comprehension run in parallel, it's easy to tack on using parList rdeepseq
, but what if the list comprehension is expressed using do
notation?
For example, this bit of code runs just fine in parallel.
entry n = [ (rowSet,finalEV rowSet,rrDone rowSet,oneRRLeft rowSet,twoRRLeft rowSet) | rowSet <- getChoiceList 13 n] `using` parList rdeepseq
where
prevFinal = getFinalEVNEW $ entry (n-1)
rrDone rowSet = (getRRDoneNEW prevFinal rowSet)
doneStripped rowSet = map (\(v,_,ev) -> (v,ev)) (rrDone rowSet)
oneRRLeft rowSet = getRRChoicesNEW (doneStripped rowSet)
oneRRStripped rowSet = map (\(v,_,ev) -> (v,ev)) (oneRRLeft rowSet)
twoRRLeft rowSet = getRRChoicesNEW (oneRRStripped rowSet)
twoRRStripped rowSet = map (\(v,_,ev) -> (v,ev)) (twoRRLeft rowSet)
finalEV rowSet = weightedEV theProbs (twoRRStripped rowSet)
But it's easier to read when expressed with do
notation. The part above, expressed in do form, and with the broader context becomes:
monster :: [[([Bool],Double,[([Int],Int,Double)],[([Int],[Bool],Double)],[([Int],[Bool],Double)])]]
monster = [ entry i | i <- [0..13] ]
where
entry 0 = [(replicate 13 False,0,[],[],[])]
entry n = go ( getFinalEVNEW ( entry (n-1) ) )
where
go prevFinal = do
rowSet <- (getChoiceList 13 n )
let rrDone = (getRRDoneNEW prevFinal rowSet)
let doneStripped = map (\(v,_,ev) -> (v,ev)) rrDone
let oneRRLeft = getRRChoicesNEW doneStripped
let oneRRStripped = map (\(v,_,ev) -> (v,ev)) oneRRLeft
let twoRRLeft = getRRChoicesNEW oneRRStripped
let twoRRStripped = map (\(v,_,ev) -> (v,ev)) twoRRLeft
let finalEV = weightedEV theProbs twoRRStripped
return (rowSet,finalEV,rrDone,oneRRLeft,twoRRLeft)
But it's unclear how to make the do form run in parallel.
Is there a straightforward way to transform a parallel list comprehension to an equivalent do form?
EDIT: The "do version" was expanded a bit for context.
A related issue is the fact the algorithm is recursive, and any attempt to parallelize entry n
should wait until entry (n-1)
is in normal form. Right?
This is a calculation for a solution to a game similar to Yahtzee. It's a "let's learn Haskell" exercise.
CodePudding user response:
The straightforward translation of the code you say works looks like this:
entry n = go ( getFinalEVNew ( entry (n-1) ) ) `using` parList rdeepseq
If you insist on attaching the using
clause to the do
block, you can either use parentheses:
go prevFinal = (do
...
) `using` parList rdeepseq
or use an indentation level between that of the where
block and the do
block:
go prevFinal = do
...
`using` parList rdeepseq
or apply using
in prefix:
go prevFinal = (`using` parList rdeepseq) $ do
...