使用以下定义:
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减少了。
答案 0 :(得分:26)
因为在第一个表达式中,第一个199
的类型为Integer
,第二个表达式为Int
。但在第二个表达式中,类型Int
和factorial 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 => a
和Num b => b
,如果愿意的话)
lenDigits
是Show 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
(<=
的右侧})也是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
而不是Integer
。 factorial (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