问题描述
我有两种形式的函数
foo :: Int -> IO String
bar :: Int -> IO Integer
基本上生活在由(->) Int
和IO
组成的函子中。
现在,具有类型
baz :: String -> Integer -> Float
我想使用类似
的应用语法将其提升到Int -> IO _
上下文中
foobarbaz :: Int -> IO Float
foobarbaz = baz <$> foo <*> bar
如果我这样做,编译器会对我大喊
Couldn't match type `IO String' with `[Char]'
Expected type: Int -> String
Actual type: Int -> IO String
就好像试图将应用实例仅用于(->) Int
。
我认为应用函子是组成的,因此我可以将应用实例用于组合函子。我错了吗?还是应该向编译器提供更多信息?
我也尝试启用TypeApplications
来明确指定要使用的函子,但我意识到我无法编写(->) Int (IO _)
。真的有办法吗?
解决方法
我认为应用函子是组成的,因此我可以将应用实例用于组合函子。
是正确的,但是假设我们采用f ~ (->) Int
,则类型为(<$>) :: (a -> b) -> (Int -> a) -> (Int -> b)
和(<*>) :: (Int -> (a -> b)) -> (Int -> a) -> (Int -> b)
,但这与类型baz
不匹配需要String
和Integer
,而foo
返回IO String
,bar
返回IO Integer
。
您可以使用liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
来提升baz
函数以使用IO
动作的结果:
import Control.Applicative(liftA2)
foobarbaz :: Int -> IO Float
foobarbaz = liftA2 baz <$> foo <*> bar
liftA2
将在这里将baz :: String -> Integer -> Float
转换为liftA2 baz :: IO String -> IO Integer -> IO Float
。因此,此函数然后与foo
和bar
的输出类型匹配。