因此,我编写了我自己的StateT实现,因为我无法让变压器在Haste中正确编译。我想要让javascript setInterval在我的状态单元中工作。这是对setInterval的ffi电话。
jsInterval :: Int -> IO () -> IO Int
jsInterval = ffi "(function(t,f){window.setInterval(f,t);})"无论如何,在将m传递给jsInterval之后,我都想不出它的结果。所以我试着使用IORefs。
interval :: Int -> StateT s IO () -> StateT s IO Int
interval i m = StateT $ \s -> do
ref <- newIORef Nothing
id_ <- jsInterval i $ do
(_, s') <- runStateT m s
writeIORef ref (Just s')
s' <- readIORef ref
return (id_, s')这不起作用是因为它保持了原来的状态。阅读发生在写之前。因此,我编写了一个函数,它可以在循环中轮询,直到编写了IORef,但是这个函数将永远挂起。
interval :: Int -> StateT s IO () -> StateT s IO Int
interval i m = StateT $ \s -> do
ref <- newIORef Nothing
id_ <- jsInterval i $ do
(_, s') <- runStateT m s
writeIORef ref (Just s')
s' <- go ref
return (id_, s')
where
go ref = do
s <- readIORef ref
case s of
Nothing -> go ref
Just s' -> return s'有可能实现这个功能吗?我尝试为MonadEvent编写一个StateT实例,但也没有成功。
发布于 2015-09-25 09:41:32
传递给FFI‘’ed jsInterval的IO操作只是一个普通的IO操作。如果您使用runStateT实现该操作,那么您只是在运行一些“本地”StateT。与封闭式代码无关。
这是回调和monad堆栈的一个通用问题--回调(从IO(的意义上说,jsInterval是回调)在其定义中选择了一个固定的单变量,它们无法推广到其他地方可能使用的其他一元效应。
由于回调--通常情况下--可以在调用函数完成并其状态被破坏之后,在不同的线程中随时调用,包括多次调用--您可以看到,这通常是很难解决的问题。
实用的答案是,正如您已经尝试过的那样,只使用一个IORef;在封闭操作中创建IORef并让回调修改它。如果愿意,仍然可以用StateT风格编写回调--只需从IORef中提取状态并将其传递给runStateT即可。您的代码没有这样做,您只是从顶层引用参数s:您需要使用IORef,如下所示:
id_ <- jsInterval i $ do
current_s <- readIORef ref
(_, new_s) <- runStateT m current_s
writeIORef ref (new_s)你不能真正使用Maybe,除非你准备好教动作m如何对付Maybe --它需要处理Nothing,所以也许你想让它具有StateT (Maybe s) IO ()类型?
第二个逻辑问题(?)使用您的代码,interval返回的interval当然不会被更改--在javascript返回其空闲循环之前不可能触发setInterval代码。
多年来,对传递回调的一般问题进行了多次讨论,请参阅:
https://mail.haskell.org/pipermail/haskell-cafe/2007-July/028501.html http://andersk.mit.edu/haskell/monad-peel/ http://blog.sigfpe.com/2011/10/quick-and-dirty-reinversion-of-control.html
等。
https://stackoverflow.com/questions/32777502
复制相似问题