Reputation: 382
I am having trouble with the function consensus in this block of code. The recursive definition of consensus is returning [Action] instead of IO [Action].
I am new to Haskell and do not understand why this is happening. I was under the impression that it was not possible remove the IO from a return value.
import System.Random (randomRIO)
import Data.Ord (comparing)
import Data.List (group, sort, maximumBy)
data Action = A | B deriving (Show, Eq, Ord)
-- Sometimes returns a random action
semiRandomAction :: Bool -> Action -> IO (Action)
semiRandomAction True a = return a
semiRandomAction False _ = do
x <- randomRIO (0, 1) :: IO Int
return $ if x == 0 then A else B
-- Creates a sublist for each a_i in ls where sublist i does not contain a_i
oneOutSublists :: [a] -> [[a]]
oneOutSublists [] = []
oneOutSublists (x:xs) = xs : map (x : ) (oneOutSublists xs)
-- Returns the most common element in a list
mostCommon :: (Ord a) => [a] -> a
mostCommon = head . maximumBy (comparing length) . group . sort
-- The function in question
consensus :: [Bool] -> [Action] -> IO [Action]
consensus [x] [action] = sequence [semiRandomAction x action]
consensus xs actions = do
let xs' = oneOutSublists xs
actions' = map (replicate $ length xs') actions
replies <- mapM (uncurry $ consensus) (zip xs' actions')
map mostCommon replies -- < The problem line
main = do
let xs = [True, False, False]
actions = [A, A, A]
result <- consensus xs actions
print result
ghc output
➜ ~ stack ghc example.hs
[1 of 1] Compiling Main ( example.hs, example.o )
example.hs:29:3: error:
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO [Action]
Actual type: [Action]
• In a stmt of a 'do' block: map mostCommon replies
In the expression:
do let xs' = oneOutSublists xs
actions' = map (replicate $ length xs') actions
replies <- mapM (uncurry $ consensus) (zip xs' actions')
map mostCommon replies
In an equation for ‘consensus’:
consensus xs actions
= do let xs' = ...
....
replies <- mapM (uncurry $ consensus) (zip xs' actions')
map mostCommon replies
|
29 | map mostCommon replies
|
Upvotes: 2
Views: 103
Reputation: 861
I think what you're looking for is
return $ map mostCommon replies
return
is a standard function that wraps values into monad.
You can think of it this way:
IO a
[] Action
(it's just another way of writing [Action]
)so there are two errors here:
[Action]
vs IO [Action]
)[]
and expected IO
) do not matchWhen you use return
function here you fix both errors.
Upvotes: 4
Reputation: 85767
consensus
is supposed to return a value of type IO [Action]
. That's what Expected type: IO [Action]
means.
However, map mostCommon replies
is an expression of type [Action]
(because map
returns a plain list, no IO
).
I was under the impression that it was not possible remove the IO from a return value.
Indeed, which is why you're getting a type error. The "cannot remove IO" thing is not a fundamental property, it just follows from the types of available operations in the standard library.
So how do we solve this?
You have a value of type IO [[Action]]
, namely mapM (uncurry $ consensus) (zip xs' actions')
. You want to apply mostCommon
to each inner list (within the outer list within IO
).
By using <-
in a do
block, you can locally "extract" values from IO a
:
replies <- mapM (uncurry $ consensus) (zip xs' actions')
-- replies :: [[Action]]
By using map
, you can apply mostCommon
to each sublist:
map mostCommon replies :: [Action]
What's missing is that you need to "rewrap" your value in IO
to make the do
block pass type-checking (each individual statement in a do
block must have the same base type, in this case IO
):
return (map mostCommon replies)
Here return :: [Action] -> IO [Action]
(or in general: return :: (Monad m) => a -> m a
).
Upvotes: 5