Haskell:使用 IO 字符串制作 IO 映射时输入错误

Haskell: Wrong typing when making an IO Map out of IO Strings

我有一个具有以下签名的函数:

Config -> IO (Map.Map String String)

用于从 Config 中提取文件路径,然后加载该文件(包含文本行),然后将这些行解析为键和值 - 这就是我需要将它们保存到地图中的原因,最好。

所以,我创建了以下函数:

let lns = liftM lines $ readFile $ getSet $ listF c in
    foldM (\m n -> liftM3 Map.insert n n m) (return Map.empty) lns

现在,我希望 Haskell 意识到我希望这是一个字符串的 IO 映射(为简单起见,我暂时不将行解析为键和值,只是将整行在那里)。

但是,我收到以下错误:

• Couldn't match type ‘Map.Map String’ with ‘IO’
  Expected type: IO (Map.Map String String)
    Actual type: Map.Map
                   String (Map.Map String (Map.Map String String))
• In the expression:
    foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
  In the expression:
    let lns = liftM lines $ readFile $ getSet $ listF c
    in foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
  In an equation for ‘getEpisodeNames’:
      getEpisodeNames c
        | listF c == NotSet = return Map.empty
        | otherwise
        = let lns = liftM lines $ readFile $ getSet $ listF c
          in foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns


• Couldn't match type ‘[Char]’ with ‘Map.Map String String’
  Expected type: Map.Map
                   String (Map.Map String (Map.Map String String))
    Actual type: Map.Map String (Map.Map String String)
• In the expression: liftM3 Map.insert n n m
  In the first argument of ‘foldM’, namely
    ‘(\ m n -> liftM3 Map.insert n n m)’
  In the expression:
    foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns


• Couldn't match type ‘[]’ with ‘Map.Map String’
  Expected type: IO (Map.Map String String)
    Actual type: IO [String]
• In the third argument of ‘foldM’, namely ‘lns’
  In the expression:
    foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns
  In the expression:
    let lns = liftM lines $ readFile $ getSet $ listF c
    in foldM (\ m n -> liftM3 Map.insert n n m) (return Map.empty) lns

由此看来,return Map.empty 似乎试图将 Map 包装在 IO monad 之外的其他东西中,老实说,这对我来说没有意义。

因为 Haskell 从不犯错,所以我很清楚我在某个地方搞砸了,我就是找不到,在哪里(虽然我很确定它在第二行,因为我已使用 ghci 中的 :t 检查 lns 的正确类型)。我还尝试将函数重写为 >>= 符号而不是 let ... in ...,但它没有帮助。感谢您的帮助。

好的,所以我实际上已经设法修复了代码,尽管我仍然不完全确定问题出在哪里,所以如果有人能指出这一点,我仍然会很感激。

所以,而不是

let lns = liftM lines $ readFile $ getSet $ listF c in
    foldM (\m n -> liftM3 Map.insert n n m) (return Map.empty) lns

我用过

(readFile . getSet $ listF c)
    >>= (\tx -> return $ lines tx)
    >>= (\lns -> return $ foldl (\m n -> Map.insert n n m) Map.empty lns)

foldM的签名是

foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

让我们看看你有什么:

return Map.empty :: IO (Map String String)
lns :: IO [String]

那绝对不匹配参数 a[b]。修复前者很容易:只需使用 Map.empty。对于后者,您需要访问 lns 中的内容,这就是绑定运算符的用途:

>>= :: m a -> (a -> m b) -> m b

因此

lns >>= foldM (…) Map.empty

但是既然你的操作是纯粹的,你还不如直接使用fmapfoldl

foldl (…) Map.empty <$> lns