中断 MonadState 中冗长的纯计算
Interrupting lengthy pure computation in MonadState
我无法掌握在 SIGINT 信号上中断冗长的纯计算的正确方法。
在下面的简单示例中,我有 slowFib
函数来模拟冗长的计算。当它在 IO
monad 中是 运行 时,我可以用 C-c 终止它(使用异步来生成 worker)。
但是,当我将计算放入 MonadState, MonadIO
堆栈时,它不再起作用...另一方面,同一堆栈中的简单 threadDelay
仍然可以终止。
代码如下:
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Data.Monoid
import Control.DeepSeq
import Control.Concurrent
import Control.Concurrent.Async
import Control.Monad.State
-- import Control.Monad.State.Strict
import System.Posix.Signals
slowFib :: Integer -> Integer
slowFib 0 = 0
slowFib 1 = 1
slowFib n = slowFib (n - 2 ) + slowFib (n - 1)
data St = St { x :: Integer } deriving (Show)
stateFib :: (MonadState St m, MonadIO m) => Integer -> m Integer
stateFib n = do
let f = slowFib n
modify $ \st -> st{x=f}
return f
stateWait :: (MonadState St m, MonadIO m) => Integer -> m Integer
stateWait n = do
liftIO $ threadDelay 5000000
return 41
interruptable n act = do
putStrLn $ "STARTING EVALUATION: " <> n
e <- async act
installHandler sigINT (Catch (cancel e)) Nothing
putStrLn "WAITING FOR RESULT"
waitCatch e
main = do
let s0 = St 0
r <- interruptable "slowFib" $ do
let f = slowFib 41
f `deepseq` return ()
return f
r <- interruptable "threadDelay in StateT" $ runStateT (stateWait 41) s0
putStrLn $ show r
r <- interruptable "slowFib in StateT" $ runStateT (stateFib 41) s0
putStrLn $ show r
我怀疑是不是跟惰性求值有关。我已经知道在第一个例子中(只有 IO
monad)我必须强制结果。否则异步计算只是 return 一个 thunk。
然而,我在 MonadState 中做类似事情的所有尝试都失败了。无论如何,它似乎更复杂,因为异步线程不会立即 return 。它一直等到计算出结果。出于某种原因,当纯计算为 "blocking".
时,我无法终止它
有什么线索吗?
PS。我的用例太添加了在使用 jupyter 包制作的自定义 Jupyter 内核中中止计算的能力。评估用户输入的函数正好在 MonadState
和 MonadIO
.
中
计算似乎在 putStrLn $ show r
上被阻止,即在 interruptable
函数之外。请注意 stateFib
不会强制结果,因此 async
几乎立即退出。整个工作会延迟到 putStrLn
尝试打印结果。尝试提前强制计算:
stateFib :: (MonadState St m, MonadIO m) => Integer -> m Integer
stateFib n = do
let f = slowFib n
modify $ \st -> st{x=f}
f `seq` return f
我无法掌握在 SIGINT 信号上中断冗长的纯计算的正确方法。
在下面的简单示例中,我有 slowFib
函数来模拟冗长的计算。当它在 IO
monad 中是 运行 时,我可以用 C-c 终止它(使用异步来生成 worker)。
但是,当我将计算放入 MonadState, MonadIO
堆栈时,它不再起作用...另一方面,同一堆栈中的简单 threadDelay
仍然可以终止。
代码如下:
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Data.Monoid
import Control.DeepSeq
import Control.Concurrent
import Control.Concurrent.Async
import Control.Monad.State
-- import Control.Monad.State.Strict
import System.Posix.Signals
slowFib :: Integer -> Integer
slowFib 0 = 0
slowFib 1 = 1
slowFib n = slowFib (n - 2 ) + slowFib (n - 1)
data St = St { x :: Integer } deriving (Show)
stateFib :: (MonadState St m, MonadIO m) => Integer -> m Integer
stateFib n = do
let f = slowFib n
modify $ \st -> st{x=f}
return f
stateWait :: (MonadState St m, MonadIO m) => Integer -> m Integer
stateWait n = do
liftIO $ threadDelay 5000000
return 41
interruptable n act = do
putStrLn $ "STARTING EVALUATION: " <> n
e <- async act
installHandler sigINT (Catch (cancel e)) Nothing
putStrLn "WAITING FOR RESULT"
waitCatch e
main = do
let s0 = St 0
r <- interruptable "slowFib" $ do
let f = slowFib 41
f `deepseq` return ()
return f
r <- interruptable "threadDelay in StateT" $ runStateT (stateWait 41) s0
putStrLn $ show r
r <- interruptable "slowFib in StateT" $ runStateT (stateFib 41) s0
putStrLn $ show r
我怀疑是不是跟惰性求值有关。我已经知道在第一个例子中(只有 IO
monad)我必须强制结果。否则异步计算只是 return 一个 thunk。
然而,我在 MonadState 中做类似事情的所有尝试都失败了。无论如何,它似乎更复杂,因为异步线程不会立即 return 。它一直等到计算出结果。出于某种原因,当纯计算为 "blocking".
时,我无法终止它有什么线索吗?
PS。我的用例太添加了在使用 jupyter 包制作的自定义 Jupyter 内核中中止计算的能力。评估用户输入的函数正好在 MonadState
和 MonadIO
.
计算似乎在 putStrLn $ show r
上被阻止,即在 interruptable
函数之外。请注意 stateFib
不会强制结果,因此 async
几乎立即退出。整个工作会延迟到 putStrLn
尝试打印结果。尝试提前强制计算:
stateFib :: (MonadState St m, MonadIO m) => Integer -> m Integer
stateFib n = do
let f = slowFib n
modify $ \st -> st{x=f}
f `seq` return f