自动重新计算结果

时间:2013-12-15 05:48:01

标签: haskell reactive-programming

基本上,我希望能够编写如下代码:

main = do
  x <- newVal (2 :: Int)
  y <- newVal (3 :: Int)
  z <- newFunc (x, y) (\(a,b) -> a * b) 
  r1 <- readVal z
  print r1 -- prints 6 (2 * 3)
  setVal x 5
  r2 <- readVal z
  print r2 -- prints 15 (5 * 3)

有人可以从头开始或从库中提供一些示例代码,以便我能够实现上述类似的代码吗?

2 个答案:

答案 0 :(得分:5)

这几乎就是STRefIORef的功能,除非我在上面的评论中提到过,否则您无法获得完全多态newFunc。你必须对liftA2做一些类似的事情。

import Data.IORef

main = do
  x <- newVal (2 :: Int)
  y <- newVal (3 :: Int)
  let z = liftIORef2 (x, y) (\(a,b) -> a * b) 
  r1 <- readVal z
  print r1 -- prints 6 (2 * 3)
  setVal x 5
  r2 <- readVal z
  print r2 -- prints 15 (5 * 3)

liftIORef2 (a, b) f = do
  a' <- readIORef a
  b' <- readIORef b
  return (f (a', b'))

newVal = newIORef

setVal = writeIORef

readVal = id

*Main> main
6
15

答案 1 :(得分:2)

可以将您的程序视为增量计算的示例,这是一个使用库Adaptive的解决方案,可用于表达此类增量计算问题。

import Control.Monad.Adaptive (newMod, readMod, inM, change, propagate, run)

main :: IO ()
main = run $ do
  x <- newMod $ return 2
  y <- newMod $ return 3
  z <- newMod $ do
    a <- readMod x
    b <- readMod y
    return (a * b)
  newMod $ do -- For observing 'z'
    r <- readMod z
    inM $ print r
  -- prints 6 (2 * 3)
  change x 5
  propagate  -- prints 15 (5 * 3)

自适应库是我的Haskell实现,它紧跟Acar,Blelloch和Harper的优秀POPL 2002论文Adaptive Functional Programming

而不是newVal,而是使用newMod,它创建了一个“可修改的”,这个计算可以跟踪readMod读取它时所依赖的其他修饰。之后,可以通过change后跟propagate更改修改,并且所有依赖于已更改的修改的修改都会按正确的顺序自动重新计算。人们可以通过在定义可修改的计算中添加副作用来观察正在发生的事情,这就是我们如何看到z会发生什么。

您可以在我为ICFP 2002撰写的论文Monads for Incremental Computing中阅读有关Haskell实现的更多信息。