You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Monad中不使用do表示法合并结果?

解决方案

当然存在这类合并方式,而且不止一种,分两种场景来说:

1. 针对任意二元合并函数(比如你的(++))

利用Applicative/Monad的特性,就能把二元函数作用在两个Monadic值上,无需do表示法:

写法一:用liftA2

来自Control.Applicative(Monad是Applicative的子类,所有Monad都适用),类型签名为:

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

你的代码可以简化成一行:

foobar = liftA2 (++) getFoo getBar

写法二:用<$>和<*>组合

这是Applicative风格的标准写法,和liftA2等价,更直观:

foobar = (++) <$> getFoo <*> getBar

<$>把普通函数(++)提升为作用在Monadic值上的函数,<*>再传入第二个Monadic值,最终得到合并后的结果。

2. 当结果类型是Monoid实例时

如果foo和bar的类型属于Monoid(比如String、List、Map等,(++)就是String/List的mappend),可以直接用Monoid的合并运算符<>(来自Data.Semigroup,Haskell 8.0及以上可用)或者mappend(来自Data.Monoid),结合Applicative写法:

import Data.Semigroup ((<>))

foobar = (<>) <$> getFoo <*> getBar

或者用liftA2:

foobar = liftA2 (<>) getFoo getBar

为什么Hoogle搜不到你要的类型?

标准库中没有单独定义Monad m, Monoid a => m a -> m a -> m a这个类型的函数,因为它可以通过liftA2 <>直接组合出来,不需要单独封装。如果你常用,可以自己定义一个运算符:

import Control.Applicative (liftA2)
import Data.Semigroup ((<>))

(<>>) :: (Monad m, Monoid a) => m a -> m a -> m a
(<>>) = liftA2 (<>)

之后就能直接用foobar = getFoo <>> getBar了。


内容的提问来源于stack exchange,提问作者Tomas Aschan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 15:48:25