在返回类型上丢失IO

Pau*_*aul 2 haskell

我在这段代码中遇到了函数共识问题.共识的递归定义是返回[Action]而不是IO [Action].

我是Haskell的新手,不明白为什么会这样.我的印象是,无法从返回值中删除IO.

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
Run Code Online (Sandbox Code Playgroud)

ghc输出

?  ~ 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
   |   
Run Code Online (Sandbox Code Playgroud)

mel*_*ene 5

consensus应该返回一个类型的值IO [Action].这Expected type: IO [Action]意味着什么.

但是,map mostCommon replies是一个类型的表达式[Action](因为map返回一个普通的列表,没有IO).

我的印象是,无法从返回值中删除IO.

实际上,这就是为什么你会遇到类型错误的原因."无法删除IO"的东西不是一个基本属性,它只是从标准库中的可用操作类型开始.

那么我们如何解决这个问题呢?

你有一个类型的值IO [[Action]],即mapM (uncurry $ consensus) (zip xs' actions').您想要应用于mostCommon每个内部列表(在外部列表中IO).

通过<-do块中使用,您可以在本地"提取"以下值IO a:

replies <- mapM (uncurry $ consensus) (zip xs' actions')
-- replies :: [[Action]]
Run Code Online (Sandbox Code Playgroud)

通过使用map,您可以申请mostCommon每个子列表:

map mostCommon replies :: [Action]
Run Code Online (Sandbox Code Playgroud)

缺少的是你需要"重新包装"你的值IO来进行do块传递类型检查(do块中的每个单独的语句必须具有相同的基类型,在这种情况下IO):

return (map mostCommon replies)
Run Code Online (Sandbox Code Playgroud)

在这里return :: [Action] -> IO [Action](或一般:) return :: (Monad m) => a -> m a.