为什么这个应用性陈述没有得到懒惰的评估,我怎么能理解为什么呢?

时间:2019-06-30 17:29:04

标签: haskell lazy-evaluation applicative io-monad

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

1 个答案:

答案 0 :(得分:4)

(<*)不使用b中的f b值,但是使用f效果,因此必须检查第二个参数。

  

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

类型为const ...

const :: a -> b -> a

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

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

...使用IO特定功能的实现,以认为有必要的任何方式组合来自两个参数的效果。

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

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

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

请注意,这不是特定于IO的问题。例如,(<*) @Maybe不会忽略其第二个参数的影响:

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