我正在使用可以为函数def mutation(L):
L.append("x")
L = []
mutation(L)
print(L)
提供的库,该函数有时会调用。
因为我的函数的输出不仅取决于它收到的a -> IO ()
作为输入,而且还取决于先前的a
,所以对我来说,编写函数{{ 1}},其中a
是无限的。
我可以写一个函数吗?
[a] -> IO ()
这会收集从回调中收到的[a]
,并将它们作为惰性无限列表传递给我的函数?
答案 0 :(得分:3)
IORef
解决方案确实是最简单的解决方案。如果您想探索纯净(但更复杂)的变体,请查看conduit。还有相同概念的其他实现,请参见Iteratee I/O,但我发现自己导管非常易于使用。
导管(AKA管道)是程序的抽象,可以接受输入和/或产生输出。这样,如果需要,它可以保持内部状态。在您的情况下,magical
将是一个 sink ,即,一个接受某种类型的输入但不产生任何输出的管道。通过将其连接到产生输出的程序 source 中,您可以完成管道,然后每当接收器请求输入时,源便会运行直到产生其输出。
在您的情况下,您大概会遇到类似的情况
magical :: Sink a IO () -- consumes a stream of `a`s, no result
magical = go (some initial state)
where
go state = do
m'input <- await
case m'input of
Nothing -> return () -- finish
Just input -> do
-- do something with the input
go (some updated state)
答案 1 :(得分:2)
这并不是您所要的,但我认为这可能足以满足您的要求。
magical :: ([a] -> IO ()) -> IO (a -> IO ())
magical f = do
list <- newIORef []
let g x = do
modifyIORef list (x:)
xs <- readIORef list
f xs -- or (reverse xs), if you need FIFO ordering
return g
因此,如果您具有函数fooHistory :: [a] -> IO ()
,则可以使用
main = do
...
foo <- magical fooHistory
setHandler foo -- here we have foo :: a -> IO ()
...
正如@danidaz在上面所述,您可能不需要magical
,但是可以直接在您的fooHistory
中玩同样的把戏,修改列表引用(IORef [a]
)。
main = do
...
list <- newIORef []
let fooHistory x = do
modifyIORef list (x:)
xs <- readIORef list
use xs -- or (reverse xs), if you need FIFO ordering
setHandler fooHistory -- here we have fooHistory :: a -> IO ()
...
答案 2 :(得分:2)
Control.Concurrent.Chan
几乎可以满足我的要求!
import Control.Monad (forever)
import Control.Concurrent (forkIO)
import Control.Concurrent.Chan
setHandler :: (Char -> IO ()) -> IO ()
setHandler f = void . forkIO . forever $ getChar >>= f
process :: String -> IO ()
process ('h':'i':xs) = putStrLn "hi" >> process xs
process ('a':xs) = putStrLn "a" >> process xs
process (x:xs) = process xs
process _ = error "Guaranteed to be infinite"
main :: IO ()
main = do
c <- newChan
setHandler $ writeChan c
list <- getChanContents c
process list
答案 3 :(得分:0)
在我看来,这似乎是图书馆设计的缺陷。您可能会考虑上游补丁程序,以便提供更通用的输入。