从monad中取出monadic函数

2 monads haskell types

Haskell有一个函数join,它"运行"monadic中的monadic动作:

join :: Monad m => m (m a) -> m a
join m = m >>= \f -> f
Run Code Online (Sandbox Code Playgroud)

我们可以用一个参数为monadic函数编写一个类似的函数:

join1 :: Monad m => m (a -> m b) -> (a -> m b)
join1 m arg1 = m >>= \f -> f arg1
Run Code Online (Sandbox Code Playgroud)

并且有两个论点:

join2 :: Monad m => m (a -> b -> m c) -> (a -> b -> m c)
join2 m arg1 arg2 = m >>= \f -> f arg1 arg2
Run Code Online (Sandbox Code Playgroud)

是否可以编写一个通用函数joinN,可以使用N个参数处理monadic函数?

J. *_*son 5

如果你真的想要,你可以用相当多的丑陋做这样的事情.

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}

import Control.Monad (join, liftM)

class Joinable m z | z -> m where
  joins :: m z -> z

instance Monad m => Joinable m (m a) where
  joins = join

instance (Monad m, Joinable m z) => Joinable m (r -> z) where
  joins m r = joins (liftM ($ r) m)
Run Code Online (Sandbox Code Playgroud)

但是,正如你所看到的,这依赖于一些不稳定的类型类魔术(尤其是不值得羡慕的UndecidableInstances).它可能更好 - 如果看起来丑陋 - 编写所有实例join1...... join10并直接导出它们.这也是base图书馆中建立的模式.

值得注意的是,在这种制度下,推论不会太顺利.例如

?> joins (return (\a b -> return (a + b))) 1 2
Overlapping instances for Joinable ((->) t0) (t0 -> t0 -> IO t0)
  arising from a use of ‘joins’
Matching instances:
  instance Monad m => Joinable m (m a)
    -- Defined at /Users/tel/tmp/ASD.hs:11:10
  instance (Monad m, Joinable m z) => Joinable m (r -> z)
    -- Defined at /Users/tel/tmp/ASD.hs:14:10
Run Code Online (Sandbox Code Playgroud)

但是如果我们给我们的论证一个明确的类型

?> let {q :: IO (Int -> Int -> IO Int); q = return (\a b -> return (a + b))}
Run Code Online (Sandbox Code Playgroud)

然后它仍然可以像我们希望的那样工作

?> joins q 1 2
3
Run Code Online (Sandbox Code Playgroud)

之所以出现这种情况,是因为仅使用类型类就很难指出你是想m在函数链的最终返回类型中使用monad 还是在函数链本身的monad (->) r操作.