Reputation: 27256
Lets assume we have
a :: IO (Maybe String)
b :: IO (Maybe String)
data Foo = Foo String String
And I want to obtain a Maybe Foo
from a
and b
.
Currently, I'm doing this
do
a' <- a
b' <- b
Foo <$> a' <*> b'
But, I feel like there should be an easier way,
(\x y -> Foo <$> x <*> y) <$> (Just <$> getLine) <*> (return Nothing)
Does the trick, but I don't want to create that ugly lambda there. Is there an operator like <$>
but with two-fold application? Or is there any way to combine a IO (Just a)
to have a single monad?
Edit:
I think the type signature is:
(Monad m, Monad n) => (a -> b -> c) -> (m (n a)) -> (m (n b)) -> (m (n c))
Edit2:
Sorry for not being clear, my data structure has more than two fields, it's actually a configuration structure having ~15 fields.
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
...
Upvotes: 3
Views: 122
Reputation: 1697
Well, while Monad
s do not compose, Applicative
s do. After wrapping all things in a suitable newtype
you get this (see Control.Compose
or Data.Functor.Compose
):
import Control.Compose
a :: (IO :. Maybe) String
b :: (IO :. Maybe) String
result :: (IO :. Maybe) Foo
result = Foo <$> a <*> b
Upvotes: 1
Reputation: 30103
Probably the simplest solution:
(liftA2 . liftA2) Foo :: IO (Maybe String) -> IO (Maybe String) -> IO (Maybe Foo)
liftM2
also works. I prefer though the weakest acceptable solution (and with the coming Applicative-Monad superclassing with GHC 7.10 this will be wholly uncontroversial).
Alternatively, if IO (Maybe a)
shows up frequently, you can use a monad transformer, and that way you can lift over any number of monads with liftA2
/ liftM2
:
import Control.Monad.Trans.Maybe
import Control.Applicative
liftA2 Foo :: MaybeT IO String -> MaybeT IO String -> MaybeT IO Foo
Upvotes: 3