在 Haste 中为 StateT monad 编写间隔函数
Write interval function for the StateT monad in Haste
所以我自己编写了 StateT
的实现,因为我无法让转换器在 Haste 中正确编译。我想让 javascript setInterval
在我的状态 monad 中工作。这是对 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'
这个功能可以实现吗?我尝试为 StateT
编写 MonadEvent
的实例,但也没有成功。
您传递给 FFI 的 IO 操作 jsInterval
只是一个普通的 IO 操作。如果您使用 runStateT
实施该操作,您只是 运行 一点点 'local' StateT
。它与封闭代码无关。
这是回调和 monad 堆栈的一般问题 - 回调(在 IO(
的意义上)jsInterval
的参数是一个回调)在它们的定义中选择了一个固定的 monad,它们无法推广到您可能在其他地方使用的其他单子效果。
因为回调 - 通常 - 可以随时调用,包括一次多次调用,在不同的线程中,在调用函数完成并且它的状态被销毁之后 - 你可以看到这是一个很难解决的问题一般解决。
实用的答案是,正如您所尝试的那样,只使用 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
返回的 s
还没有更改 - setInterval 代码不可能被触发,直到 javascript 回到空闲循环。
传递回调的一般问题多年来已经讨论过几次,请参阅:
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
等等
所以我自己编写了 StateT
的实现,因为我无法让转换器在 Haste 中正确编译。我想让 javascript setInterval
在我的状态 monad 中工作。这是对 setInterval
.
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'
这个功能可以实现吗?我尝试为 StateT
编写 MonadEvent
的实例,但也没有成功。
您传递给 FFI 的 IO 操作 jsInterval
只是一个普通的 IO 操作。如果您使用 runStateT
实施该操作,您只是 运行 一点点 'local' StateT
。它与封闭代码无关。
这是回调和 monad 堆栈的一般问题 - 回调(在 IO(
的意义上)jsInterval
的参数是一个回调)在它们的定义中选择了一个固定的 monad,它们无法推广到您可能在其他地方使用的其他单子效果。
因为回调 - 通常 - 可以随时调用,包括一次多次调用,在不同的线程中,在调用函数完成并且它的状态被销毁之后 - 你可以看到这是一个很难解决的问题一般解决。
实用的答案是,正如您所尝试的那样,只使用 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
返回的 s
还没有更改 - setInterval 代码不可能被触发,直到 javascript 回到空闲循环。
传递回调的一般问题多年来已经讨论过几次,请参阅:
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
等等