给定两个monad, Monad m
和 Monad n
,我想将 m (n a)
转换为 n (m a)
. 但似乎没有通用方法,因为 (>>=)
和 return
只处理一个monad类型,虽然 (>>=)
允许从monad中提取内容,但必须将它们打包回相同的monad类型,因此它可以是结果值 .
但是,如果我们将 m
设置为固定类型,则工作变得容易 . 以 Maybe
为例:
reorder :: (Monad n) => Maybe (n a) -> n (Maybe a)
reorder Nothing = return Nothing
reorder (Just x) = do
x' <- x
return $ Just x'
或者列表:
reorder :: (Monad n) => [n a] -> n [a]
reorder [] = return []
reorder (x:xs) = do
x' <- x
xs' <- reorder xs
return (x':xs')
不难看出,我们这里有一个模式 . 更明显的是,以 Applicative
方式编写它,它只不过是将数据构造函数应用于每个元素:
reorder (Just x) = Just <$> x
reorder (x:xs) = (:) <$> x <*> (reorder xs)
我的问题是:是否已经存在用于描述此类操作的haskell类型类,或者我是否必须自己发明轮子?
我在GHC文档中进行了简短的搜索,发现没有什么对这个主题有用 .
4 回答
Data.Traversable提供您所需要的:
GHC甚至为自动派生实例提供支持:
快速搜索hoogle的
(Monad m, Monad n) => m (n a) -> n (m a)
向我展示了几个功能(大致)符合您正在寻找的签名:Data.Traversable.sequence :: (Traversable t, Monad m) => t (m a) -> m (t a);
Data.Traversable.sequenceA :: Applicative f => t (f a) -> f (t a);
Contro.Monad.sequence :: Monad m => [m a] -> m [a](也由
Prelude
导出) .[a]
和Maybe a
都是可遍历的实例,因此您的重新排序功能只是Data.Traversable.sequence
的应用程序 . 一个人可以写,例如:但是请注意,具体的类声明是
class (Functor t, Foldable t) => Traversable t
,如果你再看一下其他两个函数的类型,那么你所寻找的东西似乎不是所有monad的通用方式m
和n
没有限制/先决条件 .它一般不能完成:一个不能做到这一点的monad的好例子是读者(或函数)monad . 这将需要以下功能可定义:
证明函数无法实现并不简单 . 但直观地说,问题是在返回的值中必须完成
IO
必须在我们知道r
参数之前完成 . 所以impossible readFile
必须先产生一个纯函数FilePath -> String
才能知道要打开哪些文件 . 显然,至少,impossible
可以't do what you' d想要它 .并非所有Monads都能以这种方式通勤 . Edward Kmett的distributive包提供类型构造函数的类型类
Distributive
,类似于你想要的(简化):为
distribute
和collect
提供了默认定义,彼此相互编写 . 我发现searching hayoo for the desired type signature这个包 .