【问题标题】:Is there an elegant way to implement this function: `(Monad m) => (s -> a -> m (s, b)) -> s -> [a] -> m [b]`有没有一种优雅的方式来实现这个函数:`(Monad m) => (s -> a -> m (s, b)) -> s -> [a] -> m [b]`
【发布时间】:2019-07-14 06:01:56
【问题描述】:

(Monad m) => (s -> a -> m (s, b)) 这样的函数根据之前的状态和当前值产生一个新的状态和一个新的值是非常频繁的。

我们可以使用不同的方法来实现对a 列表的遍历,以在给定函数f :: s -> a -> m (s, b) 的情况下生成m [b]

  • 使用Control.Monad.foldM,但代码不是特别好
  • 使用traverseStateT (WriterT m) monad,这样会更好一些

是否可以很好地利用现有的库来分解具有f 的“输出行为”的“状态定义的行为”,并在几个组合器中获得所需的遍历?

【问题讨论】:

  • m (s, [b]) 用作结果类型吗?这更容易获得。
  • 我确实可以fst <$> 如果可能的话我想避免但请分享你的想法

标签: haskell functional-programming monads monad-transformers state-monad


【解决方案1】:

直到newtype废话,我们有:

traverse @[] @(StateT s m) :: (a -> s -> m (a, s)) -> [a] -> s -> m ([b], s)

【讨论】:

  • 谢谢大牛,StateT 确实够了
【解决方案2】:

根据 Will Ness 的回答,因为我有机会在我的代码中重新排列参数,所以我可以得到以下信息

foldAccumulate :: (Monad m) => (a -> s -> m (b, s)) -> [a] -> s -> m [b]
foldAccumulate f = evalStateT . traverse (StateT . f)

这确实是一个traverse 和适当的StateT m monad,并且不需要写任何东西,我不知道为什么我没有看到:-)。谢谢!

【讨论】:

  • 如果您绝对必须避免在包装器中使用StateT,您可能想打开ScopedTypeVariables 和其他各种扩展,然后使用foldAccumulate :: forall m s a b. ...; foldAccumulate = coerce (traverse @[] @(StateT s m) @a @b) 替换(便宜,但不是完全免费) newtype 包装和展开(完全免费)仅仅强制。您还可以将Monad 约束放松为Applicative
【解决方案3】:

简单的StateT 就足够了,

foo :: Monad m => (s -> a -> m (s, b)) -> s -> [a] -> m [b]
foo g = flip (evalStateT . mapM (StateT . f))
   where
   f a s = liftM swap $ g s a

swap (a,b) = (b,a)   -- or import Data.Tuple

flipf 使作品适合,如果您必须使用您的确切类型而不是更自然的类型 a -> s -> m (b, s)

【讨论】:

  • \(a,b) -> (b,a) 有一个名字,Data.Tuple.swap
猜你喜欢
  • 2015-02-19
  • 2012-04-07
  • 2018-01-12
  • 2015-01-31
  • 2022-10-13
  • 2021-10-15
  • 1970-01-01
  • 2014-03-23
  • 1970-01-01
相关资源
最近更新 更多