如何链接 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)
一般情况下,您可能做不到。例如,如果 m
是 Const
, 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)
还是有点难看。要做的事情可能是重构 rest1
、rest2
和 rest3
函数。正如评论中指出的那样,MaybeT IO a
可以与 IO (Maybe a)
相互转换(事实上,这正是 runMaybeT
和 MaybeT
所做的)。
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
有些函数的签名如下:
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)
一般情况下,您可能做不到。例如,如果 m
是 Const
, 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)
还是有点难看。要做的事情可能是重构 rest1
、rest2
和 rest3
函数。正如评论中指出的那样,MaybeT IO a
可以与 IO (Maybe a)
相互转换(事实上,这正是 runMaybeT
和 MaybeT
所做的)。
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