2015-10-10 27 views
4

我目前正在編寫一個建立在箭頭上的FRP庫(即timeless)。但是,我遇到了一個問題:我可以爲IO Monad拍攝「快照」嗎?

如果我換箭頭內的IO動作,(Signal s IO a b在這種情況下,這是一個Kleisli箭頭),我想借一個最終的返回值的「快照」,而不是每次運行動作。例如,我有一個涉及讀取文件並解析爲某種數據結構的操作,並且當前此操作正在運行每一幀更新。我嘗試了一下,以利用Haskell的懶惰評估來防止它一次又一次地運行,但它不起作用。

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

a -> IO (b, Signal) 

每次更新中,信號本身是由新的信號所取代。現在,我認爲如果我使用Kleisli箭頭輸入IO aIO動作,我可以以某種方式將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讀取文件 「Test.txt的」。每次評估。我想找到一種方法來評估一次,並保持價值。

BTW,unsafePerformIO可能會工作,但是,正如它的名字所暗示的,它可能是「不安全」,所以我不希望使用它

回答

1

OK,我想我得到它通過增加這方面的工作信號:

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

似乎工作打算。 (更新:現在使用occursFor

經過調整後,它看起來像這樣。請注意,當我開發時,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