haskell 如何使用`runAccumT`

evrscar2  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(119)

我试图在我的程序中用AccumT替换StateT,但我不能确切地弄清楚它是如何工作的。具体来说,我不明白为什么runAccumT及其衍生物似乎忽略了它们的第二个参数。比如说,我本来以为

  1. execAccumT (add 7) 8 :: IO Int

字符串
打印15,但它打印7。我错过了什么?

zazmityj

zazmityj1#

  • 已更新 *,有一个解决方案。

首先,请注意Accum使用的是一个通用的Monoid,而不仅仅是一个sum,所以即使你的代码碰巧工作,因为它只涉及一个一元add,任何实际涉及多个累加器操作的代码都将无法进行类型检查:

  1. > execAccumT (add 7 >> add 8) 0 :: IO Int
  2. <interactive>:39:19-20: error:
  3. No instance for (Monoid Int) arising from a use of ‘>>’
  4. ...

字符串
相反,正如@DanielWagner的回答,你需要使用Sum幺半群:

  1. > execAccumT (add 7 >> add 8) 0 :: IO (Sum Int)
  2. Sum {getSum = 15}


你可以继续写add 7而不是add (Sum 7),因为Sum Int有一个Num示例,它可以自动将数字转换为和,并允许对它们进行简单的算术运算,但是如果你想在x :: Int中写add x,你需要显式地写add (Sum x)
第二,在处理累加器的初始值时确实存在一个bug,文档称之为“初始历史”。如果你写:

  1. > execAccumT (add 7 >> add 8) 999 :: IO (Sum Int)


看起来初始历史记录被忽略了,但是如果使用runAccumT同时检查look认为是最终历史记录的内容和runAccumT返回的最终历史记录,您可以看到一个差异:

  1. λ> runAccumT (add 7 >> add 8 >> look) 999 :: IO (Sum Int, Sum Int)
  2. (Sum {getSum = 1014},Sum {getSum = 15})


也就是说,look对最终历史的看法是它包括初始历史,但runAccumT返回的最终历史不包括初始历史。
目前,使用带有“mempty”初始历史的Accum是安全的,即Monoid的适当零值,如果您想从mempty以外的其他值开始,则使用初始add操作。
如果你想使用一个非mempty的历史记录,我建议导入一些隐藏的函数,并使用这些替换:

  1. import Data.Functor.Identity
  2. import Control.Monad.Trans.Accum hiding (runAccum, execAccum, runAccumT, execAccumT)
  3. runAccumT :: (Functor m, Monoid w) => AccumT w m a -> w -> m (a,w)
  4. runAccumT (AccumT f) w = (\(a, w') -> (a, w <> w')) <$> f w
  5. execAccumT :: (Monad m, Monoid w) => AccumT w m a -> w -> m w
  6. execAccumT m = fmap snd . runAccumT m
  7. runAccum :: (Monoid w) => Accum w a -> w -> (a, w)
  8. runAccum m = runIdentity . runAccumT m
  9. execAccum :: (Monoid w) => Accum w a -> w -> w
  10. execAccum m = snd . runAccum m


这些应该可以正常工作:

  1. λ> runAccumT (add 7 >> add 8 >> look) 999 :: IO (Sum Int, Sum Int)
  2. (Sum {getSum = 1014},Sum {getSum = 1014})

展开查看全部
ds97pgxw

ds97pgxw2#

只有三个基本操作检查传入的累积值:accumlooklooks。因此,如果您希望参数为runAccumT,则必须使用其中一个作为您的第一个操作来显式地请求它。

  1. > execAccumT (look >>= add) 7 :: IO (Sum Int)
  2. Sum {getSum = 7}

字符串
确实有点奇怪

相关问题