Haskell中Monad二元组双映射操作的命名与适用性问询
关于Monad二元组操作的疑问解答
你定义的这个操作:
someName :: (t1 -> m u1) -> (t2 -> m u2) -> ((t1, t2) -> m (u1, u2))
通俗来说就是:给定两个能配合bind将m t1转为m u1、m t2转为m u2的函数,返回一个针对二元组的同类函数,把(t1,t2)映射到m (u1,u2)。下面针对你的疑问逐一解答:
1. 这个操作有特定名称吗?
这个操作可以被称为**bimapM**(类比纯函数范畴的bimap,但针对Monadic函数),Haskell标准库没有直接提供,但可以基于Applicative组合子轻松实现:
bimapM :: Applicative m => (t1 -> m u1) -> (t2 -> m u2) -> (t1, t2) -> m (u1, u2) bimapM f g (x, y) = (,) <$> f x <*> g y
本质上它是对二元组两个元素分别做Monadic映射,再通过Applicative的组合能力合并结果的操作。
2. 它对所有Monad都定义良好吗?
是的,所有Monad都支持这个操作。因为从GHC 7.10开始,Monad类继承自Applicative类,所有Monad都自动拥有Applicative实例,而上述实现仅依赖Applicative的<$>和<*>操作,因此只要m是Monad,就能合法定义这个函数。
3. 和Traversable的traverse操作有什么区别?
你观察得很准确,二者核心差异在于作用范围:
traverse针对Traversable实例,对于二元组(a, b),其Traversable实例仅操作第二个元素(二元组的遍历逻辑是针对第二个类型参数的“容器”部分),所以traverse f (x, y)只会修改第二个元素,第一个元素保持原样,比如你给出的示例:ghci> f a = Just (a + 1) ghci> traverse f (0, 1) Just (0,2) ghci> traverse f ("Hello", 1) Just ("Hello",2)- 而你定义的操作是同时作用于二元组的两个元素,分别应用不同的Monadic函数后再合并结果,相当于对二元组的两个“位置”都做Monadic映射。
举个直观对比例子:
f :: Int -> Maybe Int f a = Just (a + 1) g :: String -> Maybe String g s = Just (s ++ "!") -- bimapM操作(你的定义) bimapM f g (0, "Hello") -- 结果:Just (1, "Hello!") -- traverse操作 traverse f (0, "Hello") -- 结果:Just (0, "Hello") traverse g (0, "Hello") -- 结果:Just (0, "Hello!")
内容的提问来源于stack exchange,提问作者jacobsa
相关产品推荐
相关产品推荐

