abc :: IO (Int)
abc = do
print "abc"
pure $ 10
xyz :: IO (Int)
xyz = undefined
main :: IO ()
main = do
x <- (((+) <$> abc <*> abc) <* xyz)
print x
为什么在以上xyz
中进行评估?我假设由于Haskell的懒惰性质,因此不需要评估xyz
(因此不会达到undefined
)?
我的假设基于<*
的类型:
Prelude> :t (<*)
(<*) :: Applicative f => f a -> f b -> f a
接着:
-- | Sequence actions, discarding the value of the first argument.
(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2
并且:
(<$) :: a -> f b -> f a
(<$) = fmap . const
因此f b
永远不会被使用。
有没有一种方法可以理解/调查为什么对此进行严格评估?在这方面查看GHC编译的内核会有所帮助吗?
多亏了评论中的讨论(如果我错了,请纠正我),这是由于Monad
的{{1}}实现所致,因为以下两个语句的评价似乎不同:
身份:
IO
IO:
runIdentity $ const <$> (pure 1 :: Identity Int) <*> undefined
1
答案 0 :(得分:4)
(<*)
不使用b
中的f b
值,但是使用f
效果,因此必须检查第二个参数。
为什么[
putStrLn "Hello!" *> putStrLn "World!"
同时执行,而const (print "test") (print "test2")
不执行?
类型为const
...
const :: a -> b -> a
... a
和b
都是完全参数化的,没有其他可处理的了。但是,对于(<*)
,情况就大不相同了。对于初学者来说,(<*)
是Applicative
的一种方法,因此任何写an Applicative
instance for IO
的人都可以提供具体的...
(<*) :: IO a -> IO b -> IO a
...使用IO
特定功能的实现,以认为有必要的任何方式组合来自两个参数的效果。
此外,即使(<*)
不是Applicative
的方法,它的类型...
(<*) :: Applicative f => f a -> f b -> f a
...这样,尽管a
和b
是完全参数化的,但由于f
的约束,Applicative
不是完全参数化的。它的实现可以使用Applicative
的其他方法,在大多数情况下,可以使用两个参数的效果。
请注意,这不是特定于IO
的问题。例如,(<*) @Maybe
不会忽略其第二个参数的影响:
GHCi> Just 1 <* Just 2
Just 1
GHCi> Just 1 <* Nothing
Nothing