假设我想使用ghcjs-dom创建一个事件侦听器,该事件侦听器对单击做出响应,然后将其自身删除。
我有
addListener :: (IsEventTarget t, IsEvent e)
=> t -> EventName t e -> SaferEventListener t e -> Bool -> IO ()
removeListener :: (IsEventTarget t, IsEvent e)
=> t -> EventName t e -> SaferEventListener t e -> Bool -> IO ()
添加和删除,以及
newListener :: (IsEvent e) => EventM t e () -> IO (SaferEventListener t e)
从
EventM
构造侦听器。我如何从SaferEventListener
中访问EventM
(我将稍后构造),以便在事件发生时将其删除?在JavaScript中,将命名函数表达式用作
addEventListener
的回调,然后在回调中将removeEventListener
应用于该名称。但是在这里似乎没有任何类比。还是我错过了什么? 最佳答案
使用fixIO
fixIO $ \rec -> newListener _eventm
用您的
_eventm
填充EventM
,您将能够访问最终通过名称rec
创建的事件侦听器。 rec
将是newListener
调用的结果,但是在执行之前可以被“使用”。我说“已使用”,是因为尝试用seq
或其他更强的命令强制执行该操作会导致无限循环,但是您可以很好地进行操作。fixIO
是fix
的概括:-- the essence of recursion
fix :: (a -> a) -> a
fix f = let x = f x in x
-- equivalent but less performant and less relevant
fix f = f (fix f)
-- if you have used JS's "named anonymous functions"
-- this will seem very familiar
(fix (\fact n ->
if n <= 1 then 1 else n * fact (n - 1)
)) 3 = 6
-- JS:
-- (function fact(n) {
-- if(n <= 1) { return 1; } else { return n * fact(n - 1); }
-- })(3) === 6
-- but this has more power
repeat = fix . (:)
repeat 1 = fix (1:) =
let x = 1:x in x = 1:fix (1:) = [1,1,1,1,1,1,1,1,1,1,1,1,1,1...]
fix id = let x = id x in x = let x = x in x = _|_ -- oops!
fixIO :: (a -> IO a) -> IO a
fixIO f = _ -- horrendous, unsafe code
fixIO (\xs -> return $ 1:xs) = return [1,1,1,1,1,1,1,1,1,1...]
fixIO return = fixIO (return . id) = return $ fix id = return _|_ -- oops!
fix
的想法是在实际创建函数之前使函数的最终结果可用。fixIO
的想法是在实际创建IO
函数的最终结果之前,使它可用,同时还要执行一些IO
动作。同样,fixIO
仅执行一次这些操作,这就是为什么fix
的第一个定义(仅调用f
一次)比第二个更相关的原因。反过来,
fixIO
是mfix :: MonadFix m => (a -> m a) -> m a
的一种特殊形式,其中MonadFix
是单子类(包括带有IO
的mfix = fixIO
)的单子类,它们接受这种打结方式。 GHC支持任何do
的“递归MonadFix
”表示法:{-# LANGUAGE RecursiveDo #-}
someCode = mdo ...
listener <- newListener _eventm -- can access listener in definition
...
-- or
someCode = do ...
rec listener <- newListener _eventm
...