将IO回调转换为无限列表

时间:2018-10-21 15:55:43

标签: haskell

我正在使用可以为函数def mutation(L): L.append("x") L = [] mutation(L) print(L) 提供的库,该函数有时会调用。

因为我的函数的输出不仅取决于它收到的a -> IO ()作为输入,而且还取决于先前的a,所以对我来说,编写函数{{ 1}},其中a是无限的。

我可以写一个函数吗?

[a] -> IO ()

这会收集从回调中收到的[a],并将它们作为惰性无限列表传递给我的函数?

4 个答案:

答案 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)

在我看来,这似乎是图书馆设计的缺陷。您可能会考虑上游补丁程序,以便提供更通用的输入。