鉴于两个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文档中进行了简短的搜索,发现对此主题没什么用处。
答案 0 :(得分:14)
Data.Traversable提供您正在寻找的内容:
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
GHC甚至为自动派生实例提供支持:
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
import Data.Foldable
import Data.Traversable
data List a = Nil | Cons a (List a)
deriving(Functor, Foldable, Traversable)
答案 1 :(得分:5)
对(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)
; Prelude
导出。) [a]
和Maybe a
都是可遍历的实例,因此您的重新排序功能只是Data.Traversable.sequence
的应用程序。人们可以写一下,例如:
ghci> (Data.Traversable.sequence $ Just (return 1)) :: IO (Maybe Int)
Just 1
it :: Maybe Int
ghci> (Data.Traversable.sequence $ Just ([1])) :: [Maybe Int]
[Just 1]
it :: [Maybe Int]
ghci> (Data.Traversable.sequence $ [Just 1]) :: Maybe [Int]
Just [1]
it :: Maybe [Int]
但是请注意,具体的类声明是class (Functor t, Foldable t) => Traversable t
,如果你再看一下其他两个函数的类型,那么看起来你可能无法在通用中完成它所有monad m
和n
没有限制/先决条件的方式。
答案 2 :(得分:4)
一般情况下不能这样做:一个不能做到这一点的monad的好例子是读者(或函数)monad。这将需要以下功能:
impossible :: (r -> IO a) -> IO (r -> a)
证明函数不能实现并不是直截了当的。但直观地说,问题在于,在我们知道IO
参数是之前,必须在返回的值中完成r
所做的。因此impossible readFile
在知道要打开哪些文件之前必须生成纯函数FilePath -> String
。显然,至少impossible
不能做你想要的。
答案 3 :(得分:1)
并非所有Monads都能以这种方式通勤。 Edward Kmett的distributive包为类型构造函数提供了一个类型Distributive
,类似于你想要的(简化):
class Functor g => Distributive g where
distribute :: Functor f => f (g a) -> g (f a)
collect :: Functor f => (a -> g b) -> f a -> g (f b)
为distribute
和collect
提供了彼此相对的默认定义。我在searching hayoo for the desired type signature找到了这个包。