Haskell 如何连锁a->;IO(MB)功能
有一些具有签名的函数,如:Haskell 如何连锁a->;IO(MB)功能,haskell,types,monads,Haskell,Types,Monads,有一些具有签名的函数,如: a -> IO (m b) b -> IO (m c) c -> IO (m d) a -> IO (m b) b -> IO (m c) c -> IO (m d) 我怎么把它们锁起来 a -> IO (m d) ? 实际应用:假设有一组REST端点。每个函数都返回一个值,下一个函数都需要previous返回的值作为参数 因此,从端点获取的函数如下所示: Value1 -> IO (Maybe Value2) V
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
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)
有一些具有签名的函数,如:
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
我怎么把它们锁起来
a -> IO (m d)
一般来说,您可能无法。例如,如果m
是,我不确定问这个问题是否有意义。通常,您可能期望m
成为Monad
。但是,请注意,即使m
是Monad
,其与IO
的组合也可能不是()
啊,现在你说话了!您在这里寻找的抽象是和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(可能是a)
(事实上,这正是runMaybeT
和MaybeT
所做的)
有一些具有签名的函数,如:
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
我怎么把它们锁起来
a -> IO (m d)
一般来说,您可能无法。例如,如果m
是,我不确定问这个问题是否有意义。通常,您可能期望m
成为Monad
。但是,请注意,即使m
是Monad
,其与IO
的组合也可能不是()
啊,现在你说话了!您在这里寻找的抽象是和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(可能是a)
(事实上,这正是runMaybeT
和MaybeT
所做的)
如果m有一个
可遍历的实例(可能有一个),这里有另一个选择:
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
如果m有一个可遍历的实例(可能有一个),这里有另一个选择:
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
您正在寻找Kliesli合成(>=>)
在这里另外检查IO(可能是a)~MaybeT IO a
并且MaybeT
在您正在寻找Kliesli合成(>=>)
在这里另外检查IO(可能是a)~MaybeT IO a
和MaybeT
在中定义,看起来您不需要liftIO
,并且>>=返回。join
是fmap join
。对于>>=返回的零件。加入
我想把最后一步分开。因此,如果想在这个示例中添加更多步骤(即d->IO(me)
),他们只需添加另一行>>=traverse fd。在最后一行之前加入。看起来您不需要liftIO
,并且>=返回。join
是fmap join
。对于>>=返回的零件。加入
我想把最后一步分开。因此,如果想在这个示例中添加更多步骤(即d->IO(me)
),他们只需添加另一行>>=traverse fd。在最后一行之前加入
。重构后,重新生成
rest
函数以重新获得原始函数的味道是否有意义?例如,rest1::MonadTrans mt=>Value1->mt IO Value2
?@chepner我肯定会说是的;虽然我不确定你的特殊类型是否有意义。类似于rest1::(MonadIO m,MonadPlus m)=>Value1->m Value2
会表示rest1
可能会执行一些IO(MonadIO
),但可能不会成功(MonadPlus
)。重构后,重新生成rest
函数,以重新获得原始函数的味道,这有意义吗?例如,rest1::MonadTrans mt=>Value1->mt IO Value2
?@chepner我肯定会说是的;虽然我不确定你的特殊类型是否有意义。类似于rest1::(MonadIO m,MonadPlus m)=>Value1->m Value2
的内容表示rest1
可能会执行一些IO(MonadIO
),但可能不会成功(MonadPlus
)。