Удаление текущего слушателя из 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
              ...
Другие вопросы по тегам