Alf*_*oli 22 haskell types design-patterns monad-transformers
你如何设计和构建你的monadic堆栈?我第一次需要构建一个monadic堆栈(使用变换器)来解决现实问题,但我并不完全确定堆叠变换器的顺序.如你所知,只要计算有点* -> *,基本上任何东西都可以在变换器中扮演内部monad的角色,因此有几个问题:
lift . lift . liftIO [...]?我的直觉是,如果变形金刚得到一些实例(例如MonadReader,MonadIO等,就像大多数变形金刚mtl一样),那么放置变压器的顺序并不重要.我有兴趣听取经验丰富的Haskellers关于最佳实践或经验法则的意见.
forever $ print "Thanks!"
一个.
luq*_*qui 23
这需要经验.要记住的一点是,monad变换器对它正在变换的monad一无所知,所以外层变换器被内部行为"绑定".所以
StateT s (ListT m) a
Run Code Online (Sandbox Code Playgroud)
首先,由于内在的monad,这是一个不确定的计算.然后,正常地采取非确定性,你添加状态 - 即非确定性的每个"分支"将具有其自己的状态.
Constrast with ListT (StateT s m) a,主要是有状态的 - 即整个计算只有一个状态(modulo m),并且计算将在状态中扮演"单线程"状态,因为这State意味着什么.非确定性将是最重要的 - 所以分支将能够观察先前失败的分支的状态变化.(在这个特殊的组合中,这真的很奇怪,我从来不需要它).
这是Dan Piponi的一个图表,它提供了一些有用的直觉:
我也发现扩展到实现类型很有帮助,让我感觉它是什么样的计算. ListT很难扩展,但你可以把它看作"非确定性",并且StateT很容易扩展.所以对于上面的例子,我会看一下
StateT s (ListT m) a =~ s -> ListT m (a,s)
Run Code Online (Sandbox Code Playgroud)
即它需要一个传入状态,并返回许多传出状态.这可以让您了解它是如何工作的.类似的方法是查看run堆栈所需的函数类型- 这是否与您拥有的信息和所需信息相匹配?
这是一些经验法则.它们无法替代花时间通过扩展和观察找出你真正需要的那个,但如果你只是想要在某种命令意义上寻找"添加功能",那么这可能会有所帮助.
ReaderT,WriterT和,StateT是最常见的变压器.首先,它们都是相互通勤的,所以你把它们放在哪个顺序是无关紧要的(考虑使用,RWS如果你使用这三个,那么).此外,在实践中,我常常想,这些在外面,包含"富"的变压器ListT,LogicT以及ContT在里面.
ErrorT并且MaybeT通常在上述三个之外; 让我们来看看如何MaybeT与StateT以下内容进行交互:
MaybeT (StateT s m) a =~ StateT s m (Maybe a) =~ s -> m (Maybe a, s)
StateT s (MaybeT m) a =~ s -> MaybeT m (a,s) =~ s -> m (Maybe (a,s))
Run Code Online (Sandbox Code Playgroud)
当MaybeT在外部时,即使计算失败也可观察到状态变化.当MaybeT在内部时,如果计算失败,则不会出现状态,因此必须中止在失败计算中发生的任何状态更改.你想要哪一个取决于你想要做什么 - 然而,前者对应于命令式程序员的直觉.(这不一定是需要努力的东西)
我希望这能让您了解如何考虑变换器堆栈,因此您可以使用更多工具来分析堆栈的外观.如果你将问题确定为monadic计算,那么让monad成为正确的决定是最重要的决定之一,而且并不总是那么容易.花点时间探索各种可能性.
Tik*_*vis 12
这是一个相当广泛的问题.我只想给你一些基本的想法.
首先,我建议尽可能保持基础monad多态.这将允许您在纯设置和IO设置中重用代码.这也将使您的代码更具组合性.使用各种类MonadIO也可以帮助保持代码更多态,这通常是一件好事.
需要注意的一件重要事情是,monad变换器的顺序实际上控制了它们的语义.我最喜欢的例子是将类似ListT¹的内容与EitherT错误处理相结合.如果你ListT在外面,整个计算可能会失败并出现错误.如果你EitherT在外面,那么每个分支都可能单独失败.因此,只需更改变换器的顺序,您就可以实际控制错误与非确定性交互的方式!
如果单子变压器你使用不依赖于顺序-例如,它不会多大意义的结合ReaderT和WriterT,我相信-然后就见机行事,去与任何似乎最适合您的应用程序.这种选择随着经验变得更容易.
¹:ListTfrom Control.Monad.Trans有一些问题,所以假设它ListT做得对.
| 归档时间: |
|
| 查看次数: |
834 次 |
| 最近记录: |