Почему полиморфические значения не выводятся в Haskell?
Числовые литералы имеют полиморфный тип:
*Main> :t 3
3 :: (Num t) => t
Но если я привяжу переменную к такому литералу, полиморфизм будет потерян:
x = 3
...
*Main> :t x
x :: Integer
Если я определяю функцию, с другой стороны, она, конечно, полиморфна:
f x = 3
...
*Main> :t f
f :: (Num t1) => t -> t1
Я мог бы предоставить сигнатуру типа, чтобы гарантировать, что x
остается полиморфным:
x :: Num a => a
x = 3
...
*Main> :t x
x :: (Num a) => a
Но зачем это нужно? Почему не выведен полиморфный тип?
Ответы
Ответ 1
Это ограничение мономорфизма, в котором говорится, что все значения, которые определены без параметров и не имеют явной аннотации типа, должны иметь мономорфный тип. Это ограничение можно отключить в ghc и ghci с помощью -XNoMonomorphismRestriction
.
Причиной ограничения является то, что без этого ограничения long_calculation 42
будет оцениваться дважды, тогда как большинство людей, вероятно, ожидают/хотят, чтобы его оценивали только один раз:
longCalculation :: Num a => a -> a
longCalculation = ...
x = longCalculation 42
main = print $ x + x
Ответ 2
Чтобы развернуть на sepp2k ответ немного: если вы попытаетесь скомпилировать следующее (или загрузить его в GHCi), вы получите сообщение об ошибке:
import Data.List (sort)
f = head . sort
Это нарушение ограничения мономорфизма, потому что у нас есть ограничение класса (введено sort
), но нет явных аргументов: мы (несколько таинственно) сказали, что у нас есть Ambiguous type variable
в ограничении Ord a
.
В вашем примере (let x = 3
) есть аналогичная неоднозначная переменная типа, но она не дает такой же ошибки, поскольку она сохраняется Haskell "defaulting" правила:
Любые мономорфные переменные типа, которые остаются, когда вывод типа для весь модуль завершен, считаются неоднозначными и решаются к конкретным типам, используя правила по умолчанию (раздел 4.3.4).
См. этот ответ для получения дополнительной информации о правилах по умолчанию - важно то, что они работают только для определенных числовых классов, поэтому x = 3
отлично, а f = sort
нет.
В качестве побочного примечания: если вы предпочитаете, чтобы x = 3
оказался Int
вместо Integer
, а y = 3.0
был Rational
вместо Double
, вы можете использовать "объявление по умолчанию" для переопределения стандартных правил по умолчанию:
default (Int, Rational)