读完文件后我有IO [Char],但我需要[IO Char]

时间:2014-02-25 15:25:40

标签: haskell monads

我有一个文件number.txt,其中包含大量数字,我将其读入IO String,如下所示:

readNumber = readFile "number.txt" >>= return

在另一个函数中,我想创建一个Int的列表,每个数字一个Int ......

让我们假设number.txt的内容是:

1234567890

然后我希望我的函数返回[1,2,3,4,5,6,7,8,9,0]

我尝试了几个版本mapmapM(_)liftM和,和,但是,我每次都收到几条错误消息,我可以将其减少为

Couldn't match expected type `[m0 Char]'
            with actual type `IO String'

我在磁盘上的最新版本如下:

module Main where

import Control.Monad
import Data.Char (digitToInt)

main = intify >>= putStrLn . show

readNumber = readFile "number.txt" >>= return

intify = mapM (liftM digitToInt) readNumber

所以,就我理解错误而言,我需要一些需要IO [a]并返回[IO a]的函数,但是我无法用hoogle找到这样的东西......只是反过来看存在

4 个答案:

答案 0 :(得分:7)

除了这里的其他好答案之外,最好还谈谈如何阅读 [IO Char]IO [Char]。特别是,您可以调用[IO Char]“(延迟的)IO行动的(直接)列表,其中会生成Char s”和IO [Char]“a(延迟){{ 1}}动作产生IO s的列表。

重要的部分是上面“延迟”的位置---类型Char和类型IO a之间的主要区别在于前者最好被认为是一组指令在运行时执行,最终生成a ...而后者只是a

这一阶段的区别对于理解a值的工作原理至关重要。值得注意的是,它在程序中非常流畅 - 像IOfmap这样的函数允许我们查看相位区分。例如,请考虑以下函数

(>>=)

在这里,我们通过使用一个函数修改延迟操作(foo :: IO Int -- <-- our final result is an `IO` action foo = fmap f getChar where -- <-- up here getChar is an `IO Char`, not a real one f :: Char -> Int f = Data.Char.ord -- <-- inside here we have a "real" `Char` )来构建延迟操作(foo),该函数查看在我们的延迟getChar操作后才出现的世界已经跑了。


所以让我们打结这个结并回到手头的问题。为什么你不能将IO变成IO [Char](以任何有意义的方式)?好吧,如果你正在查看一段可以访问[IO Char]的代码,那么你要做的第一件事就是潜入IO [Char]行动

IO

floob = do chars <- (getChars :: IO [Char]) ... 部分中我们可以访问...,因为我们已“进入”chars :: [Char]行动IO。这意味着到目前为止,我们必须已经运行了生成该字符列表所需的任何运行时操作。我们已经让猫离开了monad而且我们无法以任何有意义的方式重新获得它,因为我们无法返回并“解读”每个角色。

(注意:我一直在说“以任何有意义的方式”,因为我们绝对可以使用getChars将猫放回monad,但这不会让我们回到过去并且永远不会让它们在第一名。那艘船已航行。)

那么我们如何获得return类型?好吧,我们必须知道(不运行任何[IO Char])我们想做什么样的IO操作。例如,我们可以编写以下内容

IO

并立即执行类似

的操作
replicate 10 getChar :: [IO Char]

没有运行take 5 (replicate 10 getChar) 动作 - 我们的列表结构立即可用,并且在运行时有机会到达之前不会延迟。但请注意,我们必须准确了解我们要执行的IO操作的结构,以便创建类型IO。也就是说,我们可以使用另一个[IO Char]水平来窥视现实世界,以确定我们行动的参数

IO

此片段的类型为do len <- (figureOutLengthOfReadWithoutActuallyReading :: IO Int) return $ replicate len getChar 。要运行它,我们必须逐步执行IO [IO Char] 两次,我们必须让运行时执行两个 IO操作,首先确定长度和然后第二个实际操作我们的IO行动列表。

IO Char

上述函数sequence :: [IO a] -> IO [a] 是执行包含sequence个动作序列的一些结构的常用方法。我们可以用它来做我们的两阶段读取

IO

答案 1 :(得分:6)

你混淆了一些事情:

readNumber = readFile "number.txt" >>= return

回报是不必要的,只是把它留下来。

这是一个工作版本:

module Main where

import Data.Char (digitToInt)

main :: IO ()
main = intify >>= print

readNumber :: IO String
readNumber = readFile "number.txt"

intify :: IO [Int]
intify = fmap (map digitToInt) readNumber

答案 2 :(得分:2)

这样的功能不可能存在,因为您可以在不调用任何IO的情况下评估列表的长度。

可能的是:

imbue' :: IO [a] -> IO [IO a]
imbue' = fmap $ map return

当然要概括为

imbue :: (Functor f, Monad m) => m (f a) -> m (f (m a))
imbue = liftM $ fmap return

然后,您可以这样做,

quun :: IO [Char]
bar :: [IO Char] -> IO Y

main = do
   actsList <- imbue quun
   y <- bar actsLists
   ...

只是,关于使用[IO Char]的全部内容毫无意义:它完全等同于仅使用“纯值”列表的更直接的方式,只使用IO monad“outside” ;马库斯的回答显示了如何做到这一点。

答案 3 :(得分:0)

您真的需要许多不同的辅助功能吗?因为你可能只写

main = do
    file <- readFile "number.txt"
    let digits = map digitToInt file
    print digits

或者,如果您确实需要将它们分开,请尽量减少IO签名的数量:

readNumber = readFile "number.txt" --Will be IO String

intify = map digitToInt --Will be String -> [Int], not IO

main = readNumber >>= print . intify