Подтвердить что ты не робот

Противоречивое поведение лямбда-функций

Используя следующие определения:

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.

4b9b3361

Ответ 1

Так как в первом выражении первый 199 имеет тип Integer, а второй имеет Int. Но во втором выражении оба типа Int и factorial 199 не могут быть представлены типом Int.

Ответ 2

Здесь идет пошаговое рассмотрение этого вопроса.

Начнем с:

((lenDigits . factorial) 199) <= 199

В соответствии с Haskell Report...

Integer литерал представляет приложение функции 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, первый из них по-прежнему является полиморфным. В отсутствие аннотаций другого типа дефолт делает его специализированным на Integer, когда мы используем выражение в GHCi. Поэтому мы в конечном итоге получаем:

((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