utdemir
utdemir

Reputation: 27256

Two-fold fmap application

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

Answers (2)

MigMit
MigMit

Reputation: 1697

Well, while Monads do not compose, Applicatives 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

Andr&#225;s Kov&#225;cs
Andr&#225;s Kov&#225;cs

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

Related Questions