Search code examples
haskellcompositionapplicative

How to combine two composed applicative functors?


I have two composed applicative functors Maybe [Integer] and want to combine them with <$>/<*> but I am stuck with applying the applicative operation. The following does not typecheck:

(<*>) (<*>) ((<$>) ((+) <$>) $ Just [1,2,3]) $ Just [4,5,6]

Expected result:

Just [5,6,7,6,7,8,7,8,9]

The functor part works, i.e. the intermediate value passed to <*> as the first argument is Just [Integer -> Integer]. I am used to S-expressions so I have a hard time with the Haskell syntax. I know of Compose but I am interested in the mere composition wihtout abstraction.


Solution

  • As Li-yao Xia said, using liftA2 makes it a lot less confusing.

    But if you still what to see what it becomes in terms of the underlaying operations, we can expand the definition of liftA2:

    liftA2 :: (a -> b -> c) -> f a -> f b -> f c
    liftA2 f x y = f <$> x <*> y
    

    so the solution becomes

    (liftA2 . liftA2) (+) (Just [1,2,3]) (Just [4,5,6])
    = liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
    = (\f x y -> f <$> x <*> y) ((\f x y -> f <$> x <*> y) (+)) (Just [1,2,3]) (Just [4,5,6])
    = ((\f x y -> f <$> x <*> y) (+)) <$> Just [1,2,3] <*> Just [4,5,6]
    = (\x y ->  (+) <$> x <*> y) <$> Just [1,2,3] <*> Just [4,5,6]
    

    Now, this is not in point free style like your example above, and I really don't think it's helpful to convert it into point free, but here's the output from http://pointfree.io:

    ((<*>) . ((+) <$>)) <$> Just [1, 2, 3] <*> Just [4, 5, 6]
    

    we can see that this is the same by eta-expanding:

    (<*>) . ((+) <$>)
    = \x y -> ((<*>) . ((+) <$>)) x y
    = \x y -> ((<*>) $ ((+) <$>) x) y
    = \x y -> ((<*>) ((+) <$> x)) y
    = \x y -> (<*>) ((+) <$> x) y
    = \x y -> ((+) <$> x) <*> y
    = \x y -> (+) <$> x <*> y