One common technique for representing nondeterministic values is to produce a list with one element for each possibility. For example, the nondeterministic choice between Left ()
and Right ()
would be written this way:
whoKnows :: [Either () ()] -- read as: nondeterministic choice between multiple
-- possible values, each of type Either () ()
whoKnows = [Left (), Right ()]
Later, when you want to do a computation on this nondeterministic value, you can use the list's Monad
interface. For example, supposing you have a nondeterministic computation:
couldBeAnything :: Either () () -> [Int] -- read as: nondeterministic function
-- that takes an Either () () and
-- returns an Int
-- implementation left as an exercise for the reader
You could combine the two like this:
soManyPossibilities :: [Int] -- read as: nondeterministic choice between multiple
-- possible values, each of type Int
soManyPossibilities = do
leftOrRight <- whoKnows
couldBeAnything leftOrRight
You can also convert deterministic computations into ones that understand how to operate on nondeterministic values with pure
or (<$>)
. For example, if you had
soCertain :: Either () () -> Int
then you could write one of these (they mean the same thing):
fewerOptionsThisTimeMaybe :: [Int]
fewerOptionsThisTimeMaybe = do
leftOrRight <- whoKnows
pure (soCertain leftOrRight)
-- OR
fewerOptionsThisTimeMaybe = soCertain <$> whoKnows