Haskell is lazy, and ($)
doesn’t change that. The ($)
operator isn’t at all magical, and it’s a totally ordinary Haskell function†:
($) :: (a -> b) -> a -> b
f $ x = f x
由于 Haskell 是惰性的,参数在传递给函数之前不会被求值,并且($)
也不例外。所以,take 2 $ [1..]
与(take 2) [1..]
,这当然等同于take 2 [1..]
。不进行额外的评估。
现在,事实证明,有is的严格版本($)
called ($!) https://hackage.haskell.org/package/base-4.10.0.0/docs/Prelude.html#v:-36--33-,它将其参数评估为弱头正常型 (WHNF) https://stackoverflow.com/a/6889335/465378在应用该功能之前。也可以定义为普通的Haskell函数,但必须使用神奇的seq
函数作为其定义的一部分:
($!) :: (a -> b) -> a -> b
f $! x = x `seq` f x
然而,即使take 2 $! [1..]
将产生[1,2]
,不发散。为什么?出色地,$!
只将其参数评估为 WHNF,not正常形式,WHNF 可以被认为是“浅层”评估。它评估第一对缺点,但仅此而已。您可以使用以下命令看到这一点:sprint
GHCi 中的命令:
ghci> let xs = [1..] :: [Int]
ghci> xs `seq` ()
()
ghci> :sprint xs
xs = 1 : _
要递归强制一个值,您需要使用the deepseq package https://hackage.haskell.org/package/deepseq,顾名思义,深度评估一个值。它提供了一个甚至“更强”的版本($)
,称为($!!) https://hackage.haskell.org/package/deepseq-1.4.3.0/docs/Control-DeepSeq.html#v:-36--33--33-,就像($!)
但使用deepseq https://hackage.haskell.org/package/deepseq-1.4.3.0/docs/Control-DeepSeq.html#v:deepseq代替seq
。所以,take 2 $!! [1..]
will,其实是有分歧的。
† This is not strictly true in GHC, since there are some special typing rules in the compiler to help check idiomatic uses of $ when used with higher-rank types https://stackoverflow.com/a/9469942/465378. However, none of that is at all relevant here, and the simpler definition works identically.