Haskell IO操作的惰性评估
我试图用Haskell IO操作的惰性评估,haskell,Haskell,我试图用源->转换->接收风格编写代码,例如: let (|>) = flip ($) repeat 1 |> take 5 |> sum |> print 但我想使用IO来实现这一点。我有这样的印象,我的源可以是一个无限的IO操作列表,每一个都会在下游需要时得到评估。大概是这样的: -- prints the number of lines entered before "quit" is entered [getLine..] >>= takeWhile
源->转换->接收
风格编写代码,例如:
let (|>) = flip ($)
repeat 1 |> take 5 |> sum |> print
但我想使用IO
来实现这一点。我有这样的印象,我的源可以是一个无限的IO操作列表,每一个都会在下游需要时得到评估。大概是这样的:
-- prints the number of lines entered before "quit" is entered
[getLine..] >>= takeWhile (/= "quit") >>= length >>= print
我认为流媒体库可以做到这一点,但能否按照我的建议来做呢?这似乎是本着这种精神:
let (|>) = flip ($)
let (.>) = flip (.)
getContents >>= lines .> takeWhile (/= "quit") .> length .> print
使用库中的、和函数:
导入流媒体
导入符合条件的流媒体。前奏为S
计数::IO()
count=do r这里的问题是Monad
不是正确的抽象,尝试这样做会导致引用透明性被破坏的情况
首先,我们可以这样做:
module Main where
import System.IO.Unsafe (unsafePerformIO)
import Control.Monad(forM_)
lazyIOSequence :: [IO a] -> IO [a]
lazyIOSequence = pure . go where
go :: [IO a] -> [a]
go (l:ls) = (unsafePerformIO l):(go ls)
main :: IO ()
main = do
l <- lazyIOSequence (repeat getLine)
forM_ l putStrLn
这只输出Hello World
,因为我们不需要评估l
中的任何一个。但是现在考虑替换最后一行如下:
main :: IO ()
main = do
x <- lazyIOSequence (map (putStrLn . show) [1..])
seq (head x) putStrLn "Hello World"
这很糟糕,我们仅仅通过评估一个值就改变了程序的结果。这在Haskell是不应该发生的,当你评估某件事情时,它应该只是评估它,而不是改变外部世界
因此,如果您将IO操作限制为读取文件之类的内容,那么您可能能够明智地惰性地评估这些内容,因为当您读取文件时,与程序正在执行的所有其他IO操作相关的内容并不重要。但通常情况下,您不希望在IO中允许这样做,因为跳过操作或以不同的顺序执行操作可能很重要(当然,更重要的是)。即使是在延迟读取文件的情况下,如果程序中的其他内容写入文件,那么无论是在写入操作之前还是之后对该列表求值,都会影响程序的输出,这同样会破坏引用的透明度(因为求值顺序应该不重要)
因此,对于IO操作的受限子集,您可以明智地在流类型上定义Functor、Applicative和Monad以惰性方式工作,但在IO Monad中这样做通常是一个雷区,通常是不正确的。相反,您需要一个专门的流类型,并且确实在许多流类型上定义了Functor、Applicational和Monad,因此您仍然可以使用所有喜爱的函数 是的,但我认为getContents
是一个懒惰的行为。我对多重动作特别感兴趣。一般来说,是的。这正是管道
和导管
库(以及其他一些库)要做的事情。我自己也没有这方面的经验,所以示例必须来自其他人。相关人员:当然,这会起作用,但我的问题是为什么我需要流媒体库?为什么“正常”的哈斯克尔懒散还不够呢?
main :: IO ()
main = do
l <- lazyIOSequence (map (putStrLn . show) [1..])
putStrLn "Hello World"
main :: IO ()
main = do
x <- lazyIOSequence (map (putStrLn . show) [1..])
seq (head x) putStrLn "Hello World"
1
Hello World