2015-10-10 2 views
4

В настоящее время я пишу библиотеку FRP, построенную на стрелках (а именно, timeless). Тем не менее, я столкнулся с проблемой:Могу ли я взять «снимки» для IO Monad?

Если я обернуть IO действие внутри стрелки, (Signal s IO a b в этом случае, который является стрелкой Клейсли), я хотел бы сделать «снимок» окончательного возвращаемого значения, а запускать действие каждый раз. Например, у меня есть действие, связанное с чтением файла и разбором в некоторой структуре данных, и в настоящее время это действие запускает каждый кадр обновления. Я немного попытался использовать ленивую оценку Хаскелла, чтобы она не запускалась снова и снова, но это не сработало.

Концептуально Signal в основном (но не точно)

a -> IO (b, Signal) 

Каждое обновление, сам сигнал заменяется новым сигналом. Теперь, я думаю, если я подаю IO действие с типом IO a в (используя стрелки Kleisli), я могу как-то заменить 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

Хорошо, я думаю, что я получаю его работу, добавив этот сигнал:

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

Я обобщен переключатель к следующей функции (и добавляют к Prefab из timeless):

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)

После настройки, это выглядит так. Обратите внимание, что API timelessбудет меняться с яростным развитием, но, скорее всего, функции, которые я использую внизу, не будут меняться. Во всяком случае, то же самое относится к 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 
Смежные вопросы