Haskell 中的逆因果关系:从 Tardis 到 RevState

Retrocausality in Haskell: From Tardis to RevState

以下程序使用 Tardis monad 提供的向后移动状态。

{-# LANGUAGE RecursiveDo #-}

import Control.Monad.Tardis

lastOccurrence :: Int -> Tardis [Int] () Bool
lastOccurrence x = mdo
  sendPast (x : xs)
  xs <- getFuture
  return (not (elem x xs))

lastOccurrences :: [Int] -> Tardis [Int] () [Bool]
lastOccurrences xs = mapM lastOccurrence xs

main :: IO ()
main =
  print $ flip evalTardis ([], ()) $ lastOccurrences [3,4,6,7,4,3,5,7]

如何用 reverse State monad 替换 Tardis monad? 根据我的以下建议,main 永远循环而不是打印 [False,False,True,False,True,True,True,True] 与上述程序相同。

{-# LANGUAGE RecursiveDo #-}

import Control.Monad.RevState

lastOccurrence :: Int -> State [Int] Bool
lastOccurrence x = mdo
  put (x : xs)
  xs <- get
  return (not (elem x xs))

lastOccurrences :: [Int] -> State [Int] [Bool]
lastOccurrences xs = mapM lastOccurrence xs

main :: IO ()
main =
  print $ flip evalState [] $ lastOccurrences [3,4,6,7,4,3,5,7]

我现在已经下载了 TardisRevState 的源代码,我开始修改它们直到它们几乎相同:

  • 我忽略了 Trans.{Tarids,RevState} 模块之外的所有内容,这样我就不必为类型类操心了
  • 我删除了 Tardis
  • 的前向传播状态
  • 我将 Tardis 重命名为 State

在对代码重新排序后,我发现你的 Tardis-using 示例仍然有效而你的 RevState-using 示例仍然无效,它们的区别是最小的。

你问最小的区别是什么?毫不奇怪, MonadFix 实例。 Tardis has this:

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 has this:

instance MonadFix m => MonadFix (StateT s m) where
  mfix f = StateT $ \s ->
    mfix (\(x, _) -> runStateT (f x) s)

虽然它们看起来很相似,但最大的区别在于 RevState 一个在元组构造函数中是严格的,而 Tardis 一个是惰性的。 (请参阅 GHC documentation on RecursiveDo 以了解 Tardis 在传递给 mfix 的 lambda 中脱糖成无可辩驳的模式匹配)。

确实,更改 RevState 的实现,以便

instance MonadFix m => MonadFix (StateT s m) where
  mfix f = StateT $ \s -> do
    mfix (\ ~(x, _) -> runStateT (f x) s)

修复您原来的 RevState-using 程序。