我可以拍摄"快照"对于IO Monad?

时间:2015-10-10 22:31:18

标签: haskell io monads arrows

我目前正在编写一个基于Arrows的FRP库(即timeless)。但是,我遇到了一个问题:

如果我在箭头中包含IO操作,(在这种情况下为Signal s IO a b,这是一个Kleisli箭头),我想拍摄一个"快照"最终返回值,而不是每次都运行操作。例如,我有一个涉及读取文件和解析为某些数据结构的操作,并且当前此操作正在运行每个更新帧。我尝试了一些使用Haskell的懒惰评估,以防止它一次又一次地运行,但它没有用。

从概念上讲,Signal基本上(但不完全是)

a -> IO (b, Signal)

每次更新时,信号本身都会被新信号取代。现在,我想如果我使用类型IO提供IO a操作(使用Kleisli箭头),我可以某种方式将Signal替换为保存上一个操作的最终结果的其他内容。但是,我无法找到办法,因为我无法从IO中提取任何内容,只是将信号替换为常数信号似乎不会阻止该操作被重新评估。

这是一个最小的测试程序:

{-# LANGUAGE Arrows #-}

module Main where

import FRP.Timeless
import Debug.Trace

s1 :: (Monad m) => Signal s m a Int
s1 = mkConst $ trace "Signal 1" $ Just 5
s2 :: (Monad m) => Signal s m Int Int
s2 = arr $ trace "Signal 2" (+1)
s3 :: (Monad m) => Signal s m a ()
s3 = arr $ \_ -> ()

sc = mkKleisli_ $ \_ -> do
  putStrLn "SC"
  readFile "test.txt"
sp = mkKleisli_ putStrLn

box :: Signal s IO () ()
box = proc _ -> do
  file <- sc -< ()
  sp -< file
  returnA -< ()

box2 = proc _ -> do
  box -< ()

main = do
  runBox clockSession_ box2

此处,sc读取文件&#34; Test.txt&#34;。它每次评估。我想找到一种只评估一次的方法,并保留价值。

BTW,unsafePerformIO可能有用,但顾名思义,它可能是“不安全的”#34;所以我不想使用它

1 个答案:

答案 0 :(得分:1)

好的,我想我通过添加这个信号来实现它:

onceSwitch = mkPureN $ (\_ -> (Just (), mkEmpty))

我将切换推广到以下函数(并添加到Prefab的{​​{1}}):

timeless

第一次运行时输出为occursFor :: b -> Int -> Signal s m a b occursFor b n | n == 0 = mkEmpty | n > 0 = mkPureN $ \_ -> (Just b, occursFor b $ n-1) | otherwise = error "[ERROR] occursFor: Nothing occurs for less than zero times!" ,然后禁止,并且此信号:

()

首次运行后变为常量。像这样链接onceIO = SGen $ f where f _ ma = return (ma, SArr $ const ma) 动作:

IO

似乎有意。 (更新:现在使用file <- onceIO <<< sc <<< () `occursFor` 1 -< ()

经过调整后,它看起来像这样。请注意,occursFor 的API 会随着我的发展而剧烈变化,但我在下面使用的功能很可能不会改变。无论如何,同样的事情适用于timeless,这是netwire的起源,并有一些细微的变化。如果您需要制作一些应用程序,请立即使用它。

timeless