haskell - 我可以为 IO Monad 取 "snapshots"吗?

标签 haskell io monads arrows

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

如果我在箭头内包含一个 IO 操作(在本例中为 Signal s IO a b,这是一个 Kleisli 箭头),我想采取一个“最终返回值的“快照”,而不是每次都运行该操作。例如,我有一个涉及读取文件并解析为某种数据结构的操作,当前该操作正在运行每一帧更新。我尝试了一下利用 Haskell 的惰性求值来防止它一次又一次地运行,但没有成功。

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

a -> IO (b, Signal)

每次更新,信号本身都会被新信号替换。现在,我认为如果我提供一个 IO a 类型的 IO 操作(使用 Kleisli 箭头),我可以以某种方式用某些东西替换 Signal else 保存前一个操作的最终结果。但是,我找不到一种方法来做到这一点,因为我无法从 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 读取文件“Test.txt”。每次都会对其进行评估。我想找到一种方法,只评估一次,并保持值(value)。

顺便说一句,unsafePerformIO 可能会工作,但是,正如其名称所暗示的,它可能是“不安全的”,所以我不想使用它

最佳答案

好的,我想我可以通过添加这个信号来让它工作:

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

我将切换概括为以下函数(并添加到 timelessPrefab 中):

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 -< ()

似乎可以正常工作。 (更新:现在使用 occurrsFor)

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

{-# LANGUAGE Arrows #-}

module Main where

import FRP.Timeless
import Debug.Trace

sc = mkKleisli_ $ \_ -> do
  putStrLn "SC"
  return "A"
sp = mkKleisli_ putStrLn

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

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

main = do
  runBox clockSession_ box2

关于haskell - 我可以为 IO Monad 取 "snapshots"吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33059636/

相关文章:

algorithm - 寻找唯一的(只出现一次)元素haskell

haskell - WxHaskell 认为我的字符串是 1 个字符长

haskell - 检查字符串是否由平衡括号组成

python - 在 Python 中运行 shutil.rmtree(d) 后权限被拒绝执行 os.mkdir(d)

javascript - 从文件创建 JSON 对象数组

haskell - 解析器示例不适合我

haskell - `(<*>) = ap` Applicative/Monad 法则到底如何关联这两个类?

haskell - 当 Emacs 23 Haskell 编译 (C-c C-l) 进入无休止的过程时该怎么办?

haskell - Haskell 中的字节串 : should I use Put or Builder?

string - 为什么用:w and :append still overwrite the file?打开