Haskell 中的递归 IO
在 Haskell 中,我可以轻松定义一个递归函数,它接受一个值并返回一个字符串:
Prelude> let countdown i = if (i > 0) then (show i) ++ countdown (i-1) else ""
Prelude> countdown 5
"54321"
我想使用相同类型的设计从文件句柄读取可用数据。在这种特殊情况下,我需要以与 hGetContents 相同的方式读取数据,但不让句柄处于“半关闭”状态,以便我可以与使用 createProcess 打开的进程的 stdin/stdout 句柄进行循环交互
main = do
-- do work to get hin / hout handles for subprocess input / output
hPutStrLn hin "whats up?"
-- works
-- putStrLn =<< hGetContents hout
putStrLn =<< hGetLines hout
where
hGetLines h = do
readable <- hIsReadable h
if readable
then hGetLine h ++ hGetLines h
else []
:错误:
Couldn't match expected type `IO b0' with actual type `[a0]'
In the expression: hGetLine h : hGetLines h
我知道有各种库可用于完成我想要完成的任务,但因为我正在学习我的问题实际上是如何执行递归 IO。蒂亚!
In Haskell I can easily define a recursive function which takes a value and returns a string:
Prelude> let countdown i = if (i > 0) then (show i) ++ countdown (i-1) else ""
Prelude> countdown 5
"54321"
I want to use the same kind of design to read available data from a file handle. In this particular case I need to read the data in the same fashion as hGetContents, but without leaving the handle in the "semi-closed" state, so that I can loop interaction with stdin/stdout handles of a process opened with createProcess:
main = do
-- do work to get hin / hout handles for subprocess input / output
hPutStrLn hin "whats up?"
-- works
-- putStrLn =<< hGetContents hout
putStrLn =<< hGetLines hout
where
hGetLines h = do
readable <- hIsReadable h
if readable
then hGetLine h ++ hGetLines h
else []
Gives the error:
Couldn't match expected type `IO b0' with actual type `[a0]'
In the expression: hGetLine h : hGetLines h
I know there are various libraries available for accomplishing what I'm trying to accomplish, but sice I'm learning my question is really how to perform recursive IO. TIA!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
朴素的解决方案,严格和O(n)堆栈
你仍然必须使用do-表示法,这会导致这样的结果:
但是请参阅我的评论一下,这个版本的
hGetLines
太严格了!惰性流式版本
在拥有所有输入之前,它不会返回您的列表。你需要一些懒一点的东西。为此,我们有
unsafeInterleaveIO
,现在您可以开始将结果逐行流式传输到您的消费者代码:
Naive solution, strict and O(n) stack
You still have to use the do-notation, which would lead to this:
But see my comment, this version of
hGetLines
is too strict!Lazy, streaming version
It won't return your list, until it has all the input. You need something a bit lazier. For this, we have
unsafeInterleaveIO
,Now you can start streaming results line-by-line to your consumer code:
如果您检查 ghci 中
(++)
的类型,您会得到:这意味着您只能将列表附加在一起(请记住
String
是[Char] 的别名
,所以它是一个列表)。 hGetLine的类型是Handle -> IO String
,而hGetLines
的类型应该是IO [String]
,所以你不能追加这些值。(:)
的类型为a -> [a]
在这里效果更好。这同样适用于
else []
。您需要返回IO [String]
类型的值。将其更改为return []
另外,您将无法仅
putStrLn
自(=<< hGetLines h)
以来的行为您提供[String]
而不是String
,这是putStrLn
所期望的。这可以通过多种方式解决。一种是首先连接值。 putStrln 。连接=<< (hGetLines h)。或者您可以使用
mapM_ putStrLn (hGetLines h)
打印每一行。If you check the type of
(++)
in ghci you get:Meaning you can only append lists together (Remember that
String
is an alias for[Char]
, so it's a list). The type of hGetLine isHandle -> IO String
, and the type ofhGetLines
should beIO [String]
So you can not append these values.(:)
has typea -> [a]
and works better here.The same applies for
else []
. You need a value of typeIO [String]
to be returned. Change it toreturn []
Also, you won't be able to just
putStrLn
the lines since(=<< hGetLines h)
gives you[String]
and notString
which is whatputStrLn
expects.This can be solved in several ways. One is to concat the values first.
putStrln . concat =<< (hGetLines h)
. Or you can print each line usingmapM_ putStrLn (hGetLines h)
.这就是说,部分代码期望 hGetLines h 具有类型
IO a
,而另一部分则发现它具有类型[a]
。您可能希望 if 语句是:This is saying that part of the code expects hGetLines h to have type
IO a
and another part finds it to have type[a]
. You probably want your if statement to be: