Дело было вечером, делать было нечего… Загрузил я ghci, чтобы немного развлечься.
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> let f = map (3*)
Prelude> :t f
f :: [Integer] -> [Integer]
Что за фигня, подумал я. Функция f не полиморфна! Но ведь это значит, что я не смогу ее применить, например, к списку вещественных чисел!
Prelude> let a = f [1.2, 3.4]
<interactive>:4:12:
No instance for (Fractional Integer) arising from the literal `1.2'
Possible fix: add an instance declaration for (Fractional Integer)
In the expression: 1.2
In the first argument of `f', namely `[1.2, 3.4]'
In the expression: f [1.2, 3.4]
Так и есть. Попробуем с оригинальной полиморфной функцией map.
Prelude> :t map (3*)
map (3*) :: Num b => [b] -> [b]
Prelude> let b = map (3*) [1.2, 3.4]
Prelude> b
[3.5999999999999996,10.2]
Все работает, никаких неожиданностей. Неожиданностью остается обращение компилятора с функцией f. Давайте объявим аналогичную функцию f’ с явным указанием ожидаемого для нее полиморфного типа.
Prelude> :{
Prelude| let f' :: Num a => [a] -> [a]
Prelude| f' = map (3*)
Prelude| :}
Prelude> let c = f' [1.2, 3.4]
Prelude> c
[3.5999999999999996,10.2]
Ух, сработало! Не все так плохо. Но почему же компилятор по умолчанию сужает исходный полиморфный тип функции map (3*) до конкретного [Integer] -> [Integer]
в функции f? Как выяснило следствие, данный эффект обусловлен правилом ограничения мономорфным типом (monomorphism restriction), включенном в ghc по умолчанию. Главный довод для включения этого правила — обеспечение однократного вычисления значений внутри let и where, как этого обычно ожидает пользователь. Например, len в
f xs = (len, len)
where len = genericLength xs
может быть вычислена более одного раза без включения ограничения мономорфным типом. Я взял данный пример из этой статьи — в ней объясняется почему такое может произойти.
В Haskell Report проливается свет на условия применения ограничения мономорфным типом. В частности, Rule 1.(a) утверждает, что если мы свяжем все переменные в определении функции, то ее тип должен остаться полиморфным. Проверяем.
Prelude> let g x = map (3*) x
Prelude> :t g
g :: Num b => [b] -> [b]
Работает! А теперь удостоверимся, что исходное определение функции f при выключенном ограничении мономорфным типом станет полиморфным.
Prelude> :set -XNoMonomorphismRestriction
Prelude> let f'' = map (3*)
Prelude> :t f''
f'' :: Num b => [b] -> [b]
Prelude> let d = f'' [1.2, 3.4]
Prelude> d
[3.5999999999999996,10.2]
Комментариев нет:
Отправить комментарий