【发布时间】:2019-11-11 13:59:32
【问题描述】:
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 编译的核心会对此有所帮助吗?
感谢 cmets 中的讨论(如果我错了,请有人纠正我)这是由于 Monad 实现了 IO,因为以下两个语句的评估似乎不同:
身份:
runIdentity $ const <$> (pure 1 :: Identity Int) <*> undefined
1
IO:
const <$> (pure 1 :: IO Int) <*> undefined
*** Exception: Prelude.undefined
【问题讨论】:
-
(<*)不使用来自f b的b值,但它确实使用了f效果(例如,使用\x -> return x <* print x实际上会打印x) ,所以它必须检查第二个参数。 -
@ChrisStryczynski 运行时系统执行
main,并负责发现所有可从它到达的IO效果。 -
IO也不会发生这种情况。例如,Just 1 <* Just 2是Just 1,Just 1 <* Nothing是Nothing。如果不检查第二个参数,就无法弄清楚。 -
想象一下,如果有人写了
putStrLn "Hello!" *> putStrLn "World!"。输出会是什么? -
考虑
const <$> Just 1 <*> Just undefined、const <$> Just 1 <*> undefined、runIdentity $ const <$> Identity 1 <*> undefined(最后一个可能需要import Data.Functor.Identity)。所以是的,它在特定函子的应用实例的定义中。 (另见:stackoverflow.com/questions/24467803/…)
标签: haskell lazy-evaluation applicative io-monad