2013-12-15 32 views
4

基本上,我希望能夠寫出像下面的一些代碼:自動重新計算結果

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) 

有人能提供從無到有或從圖書館,讓我實現類似上面的一些示例代碼?

+0

你到底要達到什麼樣的?什麼是'newVal','newFunc','setVal'等? – Sibi

+3

@Sibi我從我的反應式編程經驗中得出的結論是,'newVal'創建一個可以/將會改變的值(認爲電子表格的一個單元格),'newFunc'創建一個「公式」並將結果存儲在'z'中,這樣結果自動更新當你'setVal'什麼 – jozefg

+0

@sibi jozefg是正確的。您定義它們,它們僅用於說明目的。預期的輸出結果應該清楚我想要實現的目標,我不介意你是否提出了一個有點不同的方法 – Clinton

回答

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 
0

希望有人會爲您提供的FRP答案,現在我會告訴你如何做一個反應系統STM。

首先,我們需要顯式聲明事務變量並設置生產者和消費者。本例中的「生產者」爲newFunc,其中xy生成新的z。 「消費者」閱讀z,如果更改則打印。

import Control.Concurrent.STM 
import Control.Concurrent 
import Control.Monad 

f = do 
    x <- newTVarIO 2 
    y <- newTVarIO 3 
    z <- newTVarIO 0 
    forkIO $ newFunc 2 3 (x,y) z 
    loop z 0 

所以這非常簡單。由於它很簡單,我們來看看線程監控和打印z。這個概念是我們記得最後一個值是什麼,如果值已經改變,只打印z。技巧在retry,這將只喚醒我們的線程,如果TVar由另一個事務寫入。

loop z zV = do 
      print zV 
      v <- atomically $ do 
        new_zV <- readTVar z 
        when (zV == new_zV) retry 
        return zV 
      loop z v 

可以進行讀compare- {重試或計算}的簡單模式是強大的,正是我們所需要的newFunc。我們將讀取所有的值和retry,條件是輸入沒有變化並且結果是正確的(根據您的需要進行調整)。

newFunc :: Int -> Int -> (TVar Int , TVar Int) -> TVar Int -> IO() 
newFunc xV yV (x, y) z = atomically $ do 
    newX <- readTVar x 
    newY <- readTVar y 
    currZ <- readTVar z 
    let result = newX * newY 
    when (newX == xV && newY == yV && currZ == result) retry 
    writeTVar z result 
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實現由我一個人阿卡爾,Blelloch和哈珀緊隨不錯popl等2002年紙Adaptive Functional Programming

代替newVal,一個使用newMod,它創建了一個「修改」,即跟蹤哪些其他modifiables的這取決於當它被readMod讀取他們的計算。之後,可以通過change後面的propagate更改可變因素,並且所有依賴於已更改的可變因子會自動按照正確的順序重新計算。人們可以通過向定義可修改性的計算添加副作用來觀察發生了什麼,這就是我們如何看到z會發生什麼情況。

你可以閱讀更多有關文件Monads for Incremental Computing Haskell的實現,我寫於2002年ICFP