返回类型上的IO丢失

时间:2018-11-15 07:39:52

标签: 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

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
   |   

2 个答案:

答案 0 :(得分: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]]

通过使用map,您可以将mostCommon应用于每个子列表:

map mostCommon replies :: [Action]

缺少的是,您需要将IO中的值“重新包装”以使do块通过类型检查(do块中的每个语句必须具有相同的基本类型,在这种情况下为IO):

return (map mostCommon replies)

这里return :: [Action] -> IO [Action](或一般来说:return :: (Monad m) => a -> m a)。

答案 1 :(得分:4)

我认为您正在寻找的是

return $ map mostCommon replies

return是将值包装为monad的标准函数。

您可以这样想:

  • 您位于IO monad内部,因此GHC希望您的函数将返回IO a
  • 您要返回[] Action(这是另一种写[Action]的方式)

所以这里有两个错误:

  • 您的实际返回类型与您的类型注释不匹配([Action]IO [Action]
  • 包装操作([]和预期的IO)的类型不匹配

在此处使用return函数时,可以同时解决这两个错误。