在 IO Maybe 内部将一个函数应用于一个值

4

我有一个类型为a -> IO (Maybe b)的函数,我想将其应用于IO (Maybe a)并得到IO (Maybe b)。我编写了一个函数来实现这个目标:

ioMaybeApply :: (a -> IO (Maybe b)) -> IO (Maybe a) -> IO (Maybe b)
ioMaybeApply f ioMaybeA = do
  maybeA <- ioMaybeA
  maybe (return Nothing) f maybeA

有一个标准的Haskell函数可以做到这一点吗?我尝试使用Hoogle搜索,但没有找到任何结果。如果没有,我的实现是否良好,或者是否可以更简单?


但是 maybe :: b -> (a -> b) -> Maybe a -> b 不是一个 IO monad 吗? - Willem Van Onsem
1
在我的代码中,maybe 返回 return Nothing(类型为 IO (Maybe b))或者 f 的返回值(也是 IO (Maybe b))。@WillemVanOnsem - Michał Perłakowski
1个回答

6

这可以通过 MaybeT Monad 变换器 实现:

GHCi> import Control.Monad.Trans.Maybe
GHCi> :t \f m -> runMaybeT (MaybeT m >>= MaybeT . f)
\f m -> runMaybeT (MaybeT m >>= MaybeT . f)
  :: Monad m => (a1 -> m (Maybe a)) -> m (Maybe a1) -> m (Maybe a)

import Control.Monad.Trans.Maybe

-- Making it look like your definition, for the sake of comparison.
ioMaybeApply :: (a -> IO (Maybe b)) -> IO (Maybe a) -> IO (Maybe b)
ioMaybeApply f ioMaybeA = runMaybeT $ do
  a <- MaybeT ioMaybeA
  MaybeT (f a)

如果您在多个地方使用此模式,则更改a -> IO (Maybe b)函数为a -> MaybeT IO b可能会更加划算--然后您可以只使用(>>=)和/或无缝的do块,而不是使用特定用途的函数。另一方面,如果这只是一个单独的问题,您可能会认为使用MaybeT有点过分; 在这种情况下,您的实现完全正常。
(值得一提的是,虽然有一个通用的嵌套函子包装器称为Compose,它具有FunctorApplicative实例,但它没有Monad实例,因为嵌套两个单独的monads并不能保证结果仍然是一个合法的Monad。因此,我们通常会采用针对每个组合量身定制的monad transformer。)

MaybeT过于复杂的情况下:有时候你可以使用(fmap . fmap) - Jeremy List
@JeremyList 是的。(其他读者请注意:在这种情况下,这相当于为MaybeT IO(或Compose IO Maybe)编写的fmap手写等效项,就像Michał的ioMaybeApplyMaybeT IO(>>=)手写等效项一样。) - duplode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接