【问题标题】:Is there a Monad which collects results and `mappend`s them?是否有一个 Monad 收集结果并将其映射到它们?
【发布时间】:2020-12-09 02:17:03
【问题描述】:

我有以下带有半群元素的阅读器模式:

runFunction :: Reader Env Element
runFunction = do
  a <- getA
  b <- getB
  c <- getC
  return $ a <> b <> c

在哪里getA :: Reader Env Element

有没有办法:

runFunction = do
  getA
  getB
  getC

我觉得我经常看到这种模式,我强制链接单子调用,最后它们变成一个元素。

注意:我不想做getA &gt;&gt;= getB &gt;&gt;= getC,因为getB 不是:: Element -&gt; Reader Env Element

感觉就像一个 State Monad,自动修改状态为 &lt;&gt;,但我不知道。

使用一元代码对我来说仍然很新鲜。

【问题讨论】:

  • do 块等同于getA &gt;&gt; getB &gt;&gt; getC。你可以使用foldr1 (&lt;&gt;) &lt;$&gt; sequenceA [getA, getB, getC]
  • 也许你需要Writer monad 的一些变体,它在隐式写入的值上使用&lt;&gt;。一个主要问题是:您希望何时访问连接的Elements 的结果?在单子计算的最后?使用Writer。在中间?您可能需要State 的一些变体。

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


【解决方案1】:

WriterT monad 转换器可用于为每个动作建立一个幺半群值。

您可以使用 lift 将您的 Reader 动作包装到 WriterT monad 转换器中,然后使用 tell 将每个动作的结果移动到 monad 的环境中,对动作进行排序,然后解包结果使用execWriterT 回到单个Reader 操作。

这就是我的意思:

import Control.Monad.Writer

wrap :: (Monad m, Monoid w) => m w -> WriterT w m ()
wrap m = lift m >>= tell

unwrap :: (Monad m) => WriterT w m a -> m w
unwrap = execWriterT

runFunction :: Reader Env Element
runFunction = unwrap $ do
    wrap getA
    wrap getB
    wrap getC

如您所见,这可以推广到任何 monad,而不仅仅是 Reader

【讨论】:

  • 哦,这真是太棒了。如果我想访问 Env,我会在调用 unwrap? runFunction = do { e &lt;- ask; unwrap $ wrap getA }?
  • @ChrisWohlert 正确,如果您使用的是mtl 包中的ask 版本Control.Monad.Reader,那么您可以直接从unwrap 区域内调用ask .否则你可以使用lift ask
【解决方案2】:
runFunction = fmap mconcat . sequence $ [getA, getB, getC]

不管 monad 是什么。

fmap mconcat . sequence
  :: (Monoid b, Monad f, Traversable t) => t (f b) -> f b

List,[],是一个 Traversable。

对于 Semigroups,有 sconcat

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-21
    相关资源
    最近更新 更多