如何链接 a -> IO (m b) 函数

How to chain a -> IO (m b) functions

有些函数的签名如下:

a -> IO (m b)
b -> IO (m c)
c -> IO (m d)

如何将它们链接到

a -> IO (m d)

?

实际应用:假设有一组 REST 端点。每个 return 一个值和下一个值都需要 return 由前一个作为参数编辑的值。

所以从端点获取的函数如下:

Value1 -> IO (Maybe Value2)
Value2 -> IO (Maybe Value3)
Value3 -> IO (Maybe Value4)

There are functions with signatures like:

a -> IO (m b)
b -> IO (m c)
c -> IO (m d)

How do I chain them in

a -> IO (m d)

一般情况下,您可能做不到。例如,如果 mConst, I'm not sure asking this even makes sense. In general, you probably expect m to be a Monad. However, note that even if m is a Monad, its composition with IO may not be (see this).

Value1 -> IO (Maybe Value2)
Value2 -> IO (Maybe Value3)
Value3 -> IO (Maybe Value4)

啊,你说话了!您在这里寻找的抽象是 MaybeT and Kleisli composition (>=>)。然后,例如,

import Control.Monad ((>=>))
import Control.Monad.Trans.Maybe (MaybeT(..))

rest1 :: Value1 -> IO (Maybe Value2)
rest2 :: Value2 -> IO (Maybe Value3)
rest3 :: Value3 -> IO (Maybe Value4)

rest4 :: Value1 -> IO (Maybe Value4)
rest4 x = runMaybeT ((MaybeT . rest1 >=> MaybeT . rest2 >=> MaybeT . rest3) x)

还是有点难看。要做的事情可能是重构 rest1rest2rest3 函数。正如评论中指出的那样,MaybeT IO a 可以与 IO (Maybe a) 相互转换(事实上,这正是 runMaybeTMaybeT 所做的)。

import Control.Monad ((>=>))
import Control.Monad.Trans.Maybe (MaybeT(..))

rest1 :: Value1 -> MaybeT IO Value2
rest2 :: Value2 -> MaybeT IO Value3
rest3 :: Value3 -> MaybeT IO Value4

rest4 :: Value1 -> MaybeT IO Value4
rest4 = rest1 >=> rest2 >=> rest3

如果 m 有一个 Traversable 的实例(Maybe 有一个)这是另一种选择:

import           Control.Monad (join)

f :: (Traversable m, Monad m)
  => (a -> IO (m b))
  -> (b -> IO (m c))
  -> (c -> IO (m d))
  -> a
  -> IO (m d)
f fa fb fc a = fa a
           >>= traverse fb
           >>= fmap join . traverse fc . join