首页 文章

重新包装monad - 任何通用方式?

提问于
浏览
13

给定两个monad, Monad mMonad 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 回答

  • 1

    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)
    
  • 4

    快速搜索hoogle的 (Monad m, Monad n) => m (n a) -> n (m a) 向我展示了几个功能(大致)符合您正在寻找的签名:

    [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的通用方式 mn 没有限制/先决条件 .

  • 14

    它一般不能完成:一个不能做到这一点的monad的好例子是读者(或函数)monad . 这将需要以下功能可定义:

    impossible :: (r -> IO a) -> IO (r -> a)
    

    证明函数无法实现并不简单 . 但直观地说,问题是在返回的值中必须完成 IO 必须在我们知道 r 参数之前完成 . 所以 impossible readFile 必须先产生一个纯函数 FilePath -> String 才能知道要打开哪些文件 . 显然,至少, impossible 可以't do what you' d想要它 .

  • 5

    并非所有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)
    

    distributecollect 提供了默认定义,彼此相互编写 . 我发现searching hayoo for the desired type signature这个包 .

相关问题