假设:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
我尝试实施Applicative IO
:
-- Learn You a Haskell is the author of all but the last line of code.
instance Applicative IO where
pure = return
a <*> b = do
f <- a -- IO (a -> b)
x <- b -- IO a. Now we need to apply (a -> b) on 'IO a' to get IO b'
case f of IO g -> return g x -- can't pattern match on IO...
这个优秀的answer解释了为什么不能使用IO构造函数。
但我怎样才能完成这个实例?
答案 0 :(得分:12)
你正确的正轨。使用IO
运算符从<-
monad中提取值后,将它们应用于函数(f x
)并使用{将其包装回IO
monad中{1}}功能。
return
当然,这些事情有效,因为m <*> n = do
f <- m
x <- n
return $ f x
已经是IO
类型类的实例。
当您Monad
f <- m
时,(a -> b)
f
同样,当您执行x <- n
时,您会在a
中获得x
。现在,您只需在x
中应用f
的值即可。应用它们之后,您可以使用IO
函数将它们包装在return
构造函数中。 (我更改了变量名称以避免混淆。)
答案 1 :(得分:6)
你很近但你犯了两个错误:
您误解了f
的类型。您已使用记号获取计算f
的结果a
,因此如果a
的类型为IO (a -> b)
,那么f
必须类型为a -> b
,而您似乎认为f
仍然是IO
计算。
你写return g x
,我认为你的意思是return (g x)
。第一个将return
应用于两个参数:g
和x
,而第二个参数将return
应用于单个参数:g x
。
简而言之,您可以通过删除案例表达式并return
f x
来轻松完成实例。
答案 2 :(得分:0)
另请参阅Control.Applicative中的WrappedMonad,特别是其源代码:http://hackage.haskell.org/package/base-4.4.1.0/docs/src/Control-Applicative.html#WrappedMonad
基本上&lt; *&gt; ap是纯粹的回归。