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