Lambda 函数的矛盾行为

Contradictory Behaviour of Lambda Functions

使用以下定义:

lenDigits n = length (show n)
factorial n = product [1..n]

我评价如下

Prelude> ((lenDigits . factorial) 199) <= 199
False
Prelude> (\i -> ((lenDigits . factorial) i) <= i) 199
True

这种行为的原因是什么?如我所见,第一个表达式与第二个表达式相同,只是减少了 lambda。

因为在第一个表达式中,第一个 199 的类型为 Integer,第二个的类型为 Int。但是在第二个表达式中,两者都具有类型 Int 并且 factorial 199 不能由类型 Int.

表示

下面是对这个问题的逐步解答。

让我们开始:

((lenDigits . factorial) 199) <= 199

根据 Haskell Report...

An integer literal represents the application of the function fromInteger to the appropriate value of type Integer.

这意味着我们的第一个表达式实际上是:

((lenDigits . factorial) (fromInteger (199 :: Integer)) 
    <= (fromInteger (199 :: Integer))

fromInteger (199 :: Integer) 本身具有多态类型 Num a => a。我们现在必须查看此类型是否专门用于整个表达式的上下文。请注意,在我们找到不是这样的原因之前,我们应该假设两次出现的 fromInteger (199 :: Integer) 的多态类型是独立的(Num a => aNum b => b,如果你愿意的话) .

lenDigitsShow a => a -> Int,所以...

(lenDigits . factorial) (fromInteger (199 :: Integer))

... <= 的左边必须是 Int。鉴于 (<=)Ord a => a -> a -> Bool<= 右边的 fromInteger (199 :: Integer) 也必须是 Int。整个表达式变为:

((lenDigits . factorial) (fromInteger (199 :: Integer)) <= (199 :: Int)

虽然第二个 199 专用于 Int,但第一个仍然是多态的。在没有其他类型注释的情况下,当我们在 GHCi 中使用表达式时,默认值使其专门化为 Integer。因此,我们最终得到:

((lenDigits . factorial) (199 :: Integer)) <= (199 :: Int)

现在,进入第二个表达式:

(\i -> ((lenDigits . factorial) i) <= i) 199

根据上面使用的相同推理,(lenDigits . factorial) i<= 的左侧)是一个 Int,因此 i([= 的右侧) 28=]) 也是一个 Int。既然如此,我们有...

GHCi> :t \i -> (lenDigits . factorial) i <= i
\i -> (lenDigits . factorial) i <= i :: Int -> Bool

... 因此将其应用于 199(实际上是 fromInteger (199 :: Integer))将其专门化为 int,给出:

((lenDigits . factorial) (199 :: Int)) <= (199 :: Int)

第一个 199 现在是 Int 而不是 Integerfactorial (199 :: Int) 溢出固定大小的 Int 类型,导致虚假结果。避免这种情况的一种方法是引入显式 fromInteger 以获得与第一种情况等效的东西:

GHCi> :t \i -> (lenDigits . factorial) i <= fromInteger i
\i -> (lenDigits . factorial) i <= fromInteger i :: Integer -> Bool
GHCi> (\i -> (lenDigits . factorial) i <= fromInteger i) 199
False