Удаление текущего слушателя из EventM
Предположим, я хочу создать, используя 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
на это имя из обратного вызова. Но ничего подобного здесь не представляется возможным. Или я что-то упустил?
1 ответ
Использование 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
...