I am currently writing an FRP library built on Arrows (namely timeless ). However, I ran into a problem:
If I transfer the action IO
inside the arrow, ( Signal s IO a b
in this case, which is the Kleisli arrow), I would like to take a “snapshot” of the final return value instead of launching the action every time. For example, I have an action related to reading a file and parsing in some data structure, and currently this action starts every update frame. I tried a bit of using a lazy Haskell evaluation so that it does not run again and again, but it doesn’t work.
Conceptually, Signal
mostly (but not exactly)
a -> IO (b, Signal)
Each update, the signal itself is replaced by a new signal. Now I think that if I pass an action IO
with type IO a
in (using the Kleisli arrows), I can somehow replace Signal
it with something else that contains the final result of the previous action. However, I cannot find a way to do this, because I cannot extract anything from it IO
, and just replacing the signal with a constant one does not seem to stop the action from re-evaluating.
This is the minimum test program:
{-# 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
Here sc
reads "Test.txt" file. He is evaluated every time. I would like to find a way to evaluate only once and keep the value.
BTW will unsafePerformIO
probably work, but as its name suggests, it is probably “unsafe”, so I don’t want to use it