【问题标题】:Why isn't this this applicative statement being lazily evaluated, and how can I understand why?为什么这个应用语句没有被懒惰地评估,我怎么能理解为什么?
【发布时间】: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)?

我的假设是基于&lt;*的类型:

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

【问题讨论】:

  • (&lt;*) 不使用来自f bb 值,但它确实使用了f 效果(例如,使用\x -&gt; return x &lt;* print x 实际上会打印x) ,所以它必须检查第二个参数。
  • @ChrisStryczynski 运行时系统执行main,并负责发现所有可从它到达的IO效果。
  • IO 也不会发生这种情况。例如,Just 1 &lt;* Just 2Just 1Just 1 &lt;* NothingNothing。如果不检查第二个参数,就无法弄清楚。
  • 想象一下,如果有人写了putStrLn "Hello!" *&gt; putStrLn "World!"。输出会是什么?
  • 考虑const &lt;$&gt; Just 1 &lt;*&gt; Just undefinedconst &lt;$&gt; Just 1 &lt;*&gt; undefinedrunIdentity $ const &lt;$&gt; Identity 1 &lt;*&gt; undefined(最后一个可能需要import Data.Functor.Identity)。所以是的,它在特定函子的应用实例的定义中。 (另见:stackoverflow.com/questions/24467803/…

标签: haskell lazy-evaluation applicative io-monad


【解决方案1】:

(&lt;*) 不使用来自f bb 值,但它确实使用了f 效果,因此它必须检查第二个参数。

为什么 [putStrLn "Hello!" *&gt; putStrLn "World!"] 会同时执行,而 const (print "test") (print "test2") 不会?

const的类型中...

const :: a -> b -> a

...ab 都是完全参数化的,没有其他需要处理的。但是,使用(&lt;*),情况就大不相同了。对于初学者来说,(&lt;*)Applicative 的一个方法,所以任何写an Applicative instance for IO 的人都可以提供一个具体的...

(<*) :: IO a -> IO b -> IO a

...实现使用IO-specific 函数以任何认为必要的方式组合来自两个参数的效果。

此外,即使(&lt;*) 不是Applicative 的方法,它的类型...

(<*) :: Applicative f => f a -> f b -> f a

... 是这样的,尽管 ab 是完全参数化的,但 f 不是,因为 Applicative 约束。它的实现可以使用Applicative 的其他方法,这些方法可以而且在大多数情况下会使用两个参数的效果。

请注意,这不是IO 特有的问题。例如,(&lt;*) @Maybe 没有忽略第二个参数的影响:

GHCi> Just 1 <* Just 2
Just 1
GHCi> Just 1 <* Nothing
Nothing

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多