I tried combining the sclv solution with luqui's unamb and would like to share my results. I will start with test cases:
list1 = [True, True, undefined, True, undefined] list2 = [undefined, False, False] list3 = concat $ replicate 500 list1 list4 = concat $ replicate 500 list2 main = mapM (print . vote) [list1, list2, list3, list4] vote :: [Bool] -> Bool
It should print
True False True False
First, I'll start with the list1 example. The voting function that passes may look like this:
voteByTrue list = sum (map bToNat list) >= threshold where threshold = (genericLength list + 1) `quot` 2
This is the same as in sclv answer. Now we need to make sum lazier so that the calculation does not interrupt when it encounters an undefined term. My first take on this was:
Zero |+ y = y Succ x |+ y = Succ (x + y) instance Num Nat where x + y = (x |+ y) `lub` (y |+ x)
Here |+ is the addition operator in the first argument, and + not strict in both of its arguments. He worked on toy examples, such as list1 , but the performance of this solution deteriorates very quickly due to exponentially inflating the number of threads (see How each + produces 2 threads, each of which calls + again, usually with the same arguments). With this kind of performance, vote list3 does not end fast enough. To deal with this, I tried to break the unamb contract and implemented the following function:
This function sorts its two arguments by the amount of information they store. It always returns a less-valued value as x and a more-valued value as y . This violates cleanliness by violating the condition of unamb arguments to equal. However, this allows us to more efficiently implement + :
instance Num Nat where x + y = x' |+ y' where (y', x') = infoMinMax xy
This allows us to take the big test ( list3 )! Now, to the false tests ... It turned out that the infoMinMax function is useful here!
vote list = voteByTrue list `maxInfo` voteByFalse list where voteByFalse = not . voteByTrue . map not maxInfo xy = snd (infoMinMax xy)
Now this allows the program to pass all four tests, although the larger ones take a few seconds. CPU usage will increase dramatically to 200% if I replaced undefined with odd (sum [1..]) , so some parallelism does occur.
However, the problem of broken purity persists. Can anyone suggest a solution in which a simple unamb ?