如何使用readFile

时间:2017-10-15 17:30:32

标签: haskell io monads

我在Haskell中读取级别文件时遇到问题。目标是读取一个简单的txt文件,其中两个数字用空格分隔,然后用逗号分隔。我一直遇到的问题是:无法匹配类型`IO' with `[]'

如果我理解正确,那么do语句应该将String拉出Monad。

readLevelFile :: FilePath -> [FallingRegion]
readLevelFile f = do
        fileContent <-  readFile f
        (map lineToFallingRegion (lines fileContent))

lineToFallingRegion :: String -> FallingRegion
lineToFallingRegion s = map textShapeToFallingShape (splitOn' (==',') s) 

textShapeToFallingShape :: String -> FallingShape
textShapeToFallingShape s = FallingShape (read $ head numbers) (read $ head 
$ tail numbers)
                      where numbers = splitOn' (==' ') s

2 个答案:

答案 0 :(得分:2)

你无法从IO中解脱出来。你可以将IO视为一个容器(事实上,IO的一些解释将它比作包含薛定谔猫的盒子)。您无法看到容器中的内容,但如果您单步进入容器,则值将变为可见。

所以这应该有效:

readLevelFile f = do
    fileContent <-  readFile f
    return (map lineToFallingRegion (lines fileContent))

然而,它没有OP中给出的类型。在do块中,fileContentString值,但整个块仍在IO容器内。

这意味着函数的返回类型不是[FallingRegion],而是IO [FallingRegion]。因此,如果您将readLevelFile的类型注释更改为

readLevelFile :: FilePath -> IO [FallingRegion]

你应该能够超越第一道障碍。

答案 1 :(得分:1)

让我们看一下显式类型的第一个函数:

readLevelFile f = do
    (fileContent :: String) <-
                  (readFile :: String -> IO String) (f :: String) :: IO String

fileContent确实属于String类型,但仅在我们正在评估的IO Monad执行期间可用。现在怎么样?

    (map lineToFallingRegion (lines fileContent))  :: [String]

现在您突然使用的表达式不是IO monad,而是列表值 - 因为列表也是monad类型,类型检查尝试将IO与{{1}统一起来}}。你真正想要的是返回这个值:

[]

现在回想起我们不能“退出”你的return (map lineToFallingRegion (lines fileContent)) :: IO [String] 类型必须是IO的IO monad - 诚实地承认它与外部世界的互动:

readLevelFile