有些签名的功能如下:
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
如何在
中链接它们a -> IO (m d)
实际应用:说有一组REST端点。每个返回值和下一个值都需要将previous返回的值作为参数。
因此从端点获取的函数如下:
Value1 -> IO (Maybe Value2)
Value2 -> IO (Maybe Value3)
Value3 -> IO (Maybe Value4)
答案 0 :(得分:6)
有些签名的功能如下:
a -> IO (m b) b -> IO (m c) c -> IO (m d)
如何在
中链接它们a -> IO (m d)
一般情况下,您可能无法做到。例如,如果m
是Const
,我不确定这是否有意义。通常,您可能希望m
为Monad
。但请注意,即使m
为Monad
,其IO
的合成也可能不是(see this)。
啊,现在你在说话!您在这里寻找的抽象是Value1 -> IO (Maybe Value2) Value2 -> IO (Maybe Value3) Value3 -> IO (Maybe Value4)
MaybeT
和Kleisli组合(>=>)
。然后,例如,
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
所做的。)
{{1}}
答案 1 :(得分:2)
如果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