Ответ 1
Теперь я загрузил источник как Tardis
, так и RevState
, и я начал их взламывать, пока они не станут почти такими же:
- Я игнорировал все, что было за пределами модулей
Trans.{Tarids,RevState}
, поэтому мне не нужно беспокоиться о классах - Я удалил вперед-распространяющееся состояние
Tardis
- Я переименовал
Tardis
вState
После небольшого переупорядочения кода я оказался в ситуации, когда ваш пример Tardis
-using по-прежнему работает, и ваш пример RevState
-using все еще не работает, и их различие минимально.
Что это за минимальная разница, спросите вы? Неудивительно, что экземпляр MonadFix
. Tardis
имеет это:
instance MonadFix m => MonadFix (TardisT bw fw m) where
mfix f = TardisT $ \s -> do
rec (x, s') <- runTardisT (f x) s
return (x, s')
тогда как RevState
имеет это:
instance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s ->
mfix (\(x, _) -> runStateT (f x) s)
Пока они кажутся похожими, большая разница в том, что RevState
один строк в конструкторе кортежа, тогда как Tardis
- ленивый. (см., например, документацию GHC на RecursiveDo
, чтобы увидеть, что Tardis
один desugars в неопровержимое совпадение шаблона в лямбда, переданном в mfix
).
Действительно, меняя реализацию RevState
так, чтобы
instance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s -> do
mfix (\ ~(x, _) -> runStateT (f x) s)
исправляет исходную программу RevState
-using.