Reputation: 9008
I have two functions of the form
foo :: Int -> IO String
bar :: Int -> IO Integer
which basically live in the functor given by the composition of (->) Int
and IO
.
Now, having a function of type
baz :: String -> Integer -> Float
I would like to lift it to the Int -> IO _
context using applicative syntax like
foobarbaz :: Int -> IO Float
foobarbaz = baz <$> foo <*> bar
If I do this the compiler yells at me with
Couldn't match type `IO String' with `[Char]'
Expected type: Int -> String
Actual type: Int -> IO String
as if it was trying to use the applicative instance only for (->) Int
.
I thought applicative functors composed, so that I could use the applicative instance for the composed functor. Am I wrong? Or should I just provide more information to the compiler?
I tried also to enable TypeApplications
to specify explicitly the functor I want to use, but I realized that I can't write (->) Int (IO _)
. Is there actually a way to do it?
Upvotes: 2
Views: 176
Reputation: 476557
I thought applicative functors composed, so that I could use the applicative instance for the composed functor.
That is correct, but imagine that we take f ~ (->) Int
, then the types are (<$>) :: (a -> b) -> (Int -> a) -> (Int -> b)
, and (<*>) :: (Int -> (a -> b)) -> (Int -> a) -> (Int -> b)
, but this does not match with the types since, baz
requires a String
and an Integer
, whereas foo
returns an IO String
and bar
an IO Integer
.
You can make use of liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
to lift the baz
function to use the result of IO
actions:
import Control.Applicative(liftA2)
foobarbaz :: Int -> IO Float
foobarbaz = liftA2 baz <$> foo <*> bar
liftA2
will here thus convert a baz :: String -> Integer -> Float
, into a liftA2 baz :: IO String -> IO Integer -> IO Float
. This is thus a function that then matches with the output types of foo
and bar
.
Upvotes: 3