让我们假设我们有
a :: IO (Maybe String)
b :: IO (Maybe String)
data Foo = Foo String String
我希望从Maybe Foo
和a
获得b
。
目前,我正在这样做
do
a' <- a
b' <- b
Foo <$> a' <*> b'
但是,我觉得应该有一个更简单的方法,
(\x y -> Foo <$> x <*> y) <$> (Just <$> getLine) <*> (return Nothing)
诀窍,但我不想在那里创造那个丑陋的lambda。是否有像<$>
这样的运算符,但有两倍的应用程序?或者有没有办法将IO (Just a)
合并为一个monad?
修改
我认为类型签名是:
(Monad m, Monad n) => (a -> b -> c) -> (m (n a)) -> (m (n b)) -> (m (n c))
EDIT2:
很抱歉不清楚,我的数据结构有两个以上的字段,它实际上是一个包含~15个字段的配置结构。
cfg <- Conf.load [ Conf.Required cfile ]
foo1 <- (Conf.lookup cfg "foo1" :: Maybe String )
foo2 <- Conf.lookup cfg "foo2"
foo3 <- Conf.lookup cfg "foo3"
foo4, foo5, foo6...
return $ Conf <$> foo1
<*> foo2
<*> foo3
<*> foo4
...
答案 0 :(得分:3)
可能是最简单的解决方案:
(liftA2 . liftA2) Foo :: IO (Maybe String) -> IO (Maybe String) -> IO (Maybe Foo)
liftM2
也有效。我更喜欢尽可能最弱的可接受的解决方案(并且随着即将到来的Applicative-Monad对GHC 7.10进行超级分类,这将完全没有争议)。
或者,如果经常显示IO (Maybe a)
,您可以使用monad变换器,这样您就可以使用liftA2
/ liftM2
提升任意数量的monad:
import Control.Monad.Trans.Maybe
import Control.Applicative
liftA2 Foo :: MaybeT IO String -> MaybeT IO String -> MaybeT IO Foo
答案 1 :(得分:1)
好吧,虽然Monad
没有撰写,Applicative
也可以。在将所有内容包装到合适的newtype
后,您可以获得此内容(请参阅Control.Compose
或Data.Functor.Compose
):
import Control.Compose
a :: (IO :. Maybe) String
b :: (IO :. Maybe) String
result :: (IO :. Maybe) Foo
result = Foo <$> a <*> b