Потеря IO по типу возврата

У меня возникают проблемы с консенсусом функции в этом блоке кода. Рекурсивное определение консенсуса возвращает [Действие] вместо ИО [Действие] .

Я новичок в 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 ответа


Я думаю, что вы ищете

return $ map mostCommon replies

return - стандартная функция, которая переносит значения в монаду.

Вы можете думать об этом так:

  • вы находитесь в монаде IO, поэтому GHC ожидает, что ваша функция вернет IO a
  • вы возвращаетесь [] Action (это еще один способ написания [Action] )

поэтому здесь есть две ошибки:

  • ваш фактический тип возврата не соответствует аннотации вашего типа ( [Action] против IO [Action] )
  • типы, обертывающие действие ( [] и ожидаемый IO ), не совпадают

Когда вы используете функцию return здесь, вы исправляете обе ошибки.


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 должен иметь тот же базовый тип, в этом случае IO ):

return (map mostCommon replies)

Здесь return :: [Action] -> IO [Action] (или вообще: return :: (Monad m) => a -> ma ).


Есть идеи?

10000