利用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
...