为什么这个应用语句没有被延迟评估,我怎么能理解为什么?
Why isn't this this applicative statement being lazily evaluated, and how can I understand why?
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 编译的核心对此有帮助吗?
感谢评论中的讨论(如果我错了,请有人纠正我)这是由于 IO
的 Monad
实现,因为以下两个语句似乎评估不同的是:
身份:
runIdentity $ const <$> (pure 1 :: Identity Int) <*> undefined
1
输入输出:
const <$> (pure 1 :: IO Int) <*> undefined
*** Exception: Prelude.undefined
(<*)
不使用 f b
中的 b
值,但它确实使用 f
效果,因此它必须检查第二个参数。
Why does [putStrLn "Hello!" *> putStrLn "World!"
] execute both while const (print "test") (print "test2")
does not?
类型为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
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 编译的核心对此有帮助吗?
感谢评论中的讨论(如果我错了,请有人纠正我)这是由于 IO
的 Monad
实现,因为以下两个语句似乎评估不同的是:
身份:
runIdentity $ const <$> (pure 1 :: Identity Int) <*> undefined
1
输入输出:
const <$> (pure 1 :: IO Int) <*> undefined
*** Exception: Prelude.undefined
(<*)
不使用 f b
中的 b
值,但它确实使用 f
效果,因此它必须检查第二个参数。
Why does [
putStrLn "Hello!" *> putStrLn "World!"
] execute both whileconst (print "test") (print "test2")
does not?
类型为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