Two-time fmap application
Let's pretend that
a :: IO (Maybe String)
b :: IO (Maybe String)
data Foo = Foo String String
And I want to get Maybe Foo
out of a
and b
.
I am currently doing this
do
a' <- a
b' <- b
Foo <$> a' <*> b'
But I feel like there must be an easier way,
(\x y -> Foo <$> x <*> y) <$> (Just <$> getLine) <*> (return Nothing)
There is a trick, but I don't want to create this ugly lambda. Is there a type operator <$>
but with a double appended? Or is there a way to combine IO (Just a)
with one monad?
Edit:
I think a signature like:
(Monad m, Monad n) => (a -> b -> c) -> (m (n a)) -> (m (n b)) -> (m (n c))
Edit2:
Sorry for the obscurity, my data structure has more than two fields, it is actually a configuration structure with ~ 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
...
source to share
Perhaps the simplest solution:
(liftA2 . liftA2) Foo :: IO (Maybe String) -> IO (Maybe String) -> IO (Maybe Foo)
liftM2
also works. I prefer at least the weakest acceptable solution (and with the upcoming applicative-monadic superclassing since GHC 7.10 it will be completely indisputable).
Alternatively, if IO (Maybe a)
it appears frequently, you can use a monad transformer, and this way you can raise 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
source to share
Well, Monad
not to compose, Applicative
do. After wrapping all things in a suitable one, 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
source to share