是否有一个懒惰的Session IO Monad?

时间:2014-11-18 18:39:36

标签: haskell

由于某些高度固定的开销(如数据包标头或建立连接),您需要执行一系列操作,这些操作更倾向于以块的形式执行。限制是有时下一个操作取决于前一个操作的结果,在这种情况下,所有挂起的操作都会立即执行。

示例:

mySession :: Session IO ()
    a <- readit  -- nothing happens yet
    b <- readit  -- nothing happens yet
    c <- readit  -- nothing happens yet
    if a       -- all three readits execute because we need a
      then write "a"
      else write "..."
    if b || c  -- b and c already available
      ...

这让我想起了如此多的Haskell概念,但我不能指责它。

当然,你可以做一些明显的事情:

[a,b,c] <- batch([readit, readit, readit])

但是我想隐藏用户的分块事实以获得光滑的目的。

不确定Session是否是正确的词。也许你可以建议一个更好的? (数据包,批处理,块和延迟会浮现在脑海中。)

更新

我觉得我昨晚在手机上看到了一个非常好的答案,但是当我今天回来寻找它时,它已经消失了。我在做梦吗?

3 个答案:

答案 0 :(得分:3)

我认为你不能完全按照自己的意愿行事,因为你所描述的利用haskell的懒惰评估来评估a强制计算bc的行动,并且没有办法seq未指定的值。

我能做的就是破解一个monad变换器,它延迟了通过>>排序的动作,以便它们可以一起执行:

data Session m a = Session { pending :: [ m () ], final :: m a }                    

runSession :: Monad m => Session m a -> m a                                         
runSession (Session ms ma) = foldr (flip (>>)) (return ()) ms >> ma                            

instance Monad m => Monad (Session m) where                                         
  return = Session [] . return                                                      
  s >>= f = Session [] $ runSession s >>= (runSession . f)                          
  (Session ms ma) >> (Session ms' ma') =
    Session (ms' ++ (ma >> return ()) : ms) ma'

这违反了某些monad laws,,但允许您执行以下操作:

 liftIO :: IO a -> Session IO a  
 liftIO = Session []             

 exampleSession :: Session IO Int
 exampleSession = do             
   liftIO $ putStrLn "one"       
   liftIO $ putStrLn "two"       
   liftIO $ putStrLn "three"     
   liftIO $ putStrLn "four"      
   trace "five" $ return 5       

并获取

ghci> runSession exampleSession
five
one
two
three
four
5
ghci> length (pending exampleSession)
4

答案 1 :(得分:1)

这与Haxl非常相似。

欲了解更多信息:

答案 2 :(得分:0)

您可以使用unsafeInterleaveIO功能。这是一个危险的功能,如果不仔细使用,可能会给您的程序带来错误,但它会满足您的要求。

您可以将它插入到示例代码中,如下所示:

lazyReadits :: IO [a]
lazyReadits = unsafeInterleaveIO $ do
  a <- readit
  r <- lazyReadits
  return (a:r)

unsafeInterleaveIO使整个行动变得懒惰,但一旦开始评估它就会评估它是否严格。这意味着在上面的示例中:readit将在测试返回的列表是否为空时运行。如果我改为使用mapM unsafeInterleaveIO (replicate 3 readit),那么readit只会在评估列表的实际元素时运行,这会使列表的内容依赖于其元素的顺序检查,这是unsafeInterleaveIO如何引入错误的一个例子。