Lambda函数的矛盾行为

时间:2016-12-08 16:37:06

标签: haskell lambda evaluation

使用以下定义:

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

我评估以下

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

这种行为的原因是什么?正如我所看到的,第一个表达式与第二个表达式相同,其中lambdas减少了。

2 个答案:

答案 0 :(得分:26)

因为在第一个表达式中,第一个199的类型为Integer,第二个表达式为Int。但在第二个表达式中,类型Intfactorial 199都不能由Int类型表示。

答案 1 :(得分:8)

以下逐步介绍这个问题。

让我们开始:

((lenDigits . factorial) 199) <= 199

根据Haskell Report ...

  

整数文字表示函数fromInteger应用于类型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 -> BoolfromInteger (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<=的右侧})也是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