Почему f = (+) не требует аннотации типа?
Я имею в виду, например,
f :: (Enum a) => a -> a --without this line, there would be an error
f = succ
Это потому, что succ
требует, чтобы его параметр был перечислимым (succ :: (Enum a) => a -> a
)
но для (+)
f = (+) --ok
Хотя объявление (+)
(+) :: (Num a) => a –> a –> a
.
Я имею в виду, почему мне не нужно объявлять f
как f :: (Num a) => a –> a –> a
?
Ответы
Ответ 1
Из-за дефолта. Num
является классом типа "defaultable", что означает, что если вы оставите его без ограничений, компилятор сделает несколько разумных догадок о том, какой тип вы хотели использовать для него. Попробуйте поместить это определение в модуль, а затем запустите
:t f
в ghci
; он должен сказать вам (IIRC) f :: Integer -> Integer -> Integer
. Компилятор не знал, какой a
вы хотели использовать, поэтому он догадался Integer
; и так как это сработало, все прошло с этой догадкой.
Почему он не вывел полиморфный тип для f
? Из-за ужасного ограничения мономорфизма [1]. Когда компилятор видит
f = (+)
он думает, что "f
- это значение", что означает, что ему нужен один (мономорфный) тип. Eta-expand определение
f x = (+) x
и вы получите полиморфный тип
f :: Num a => a -> a -> a
и аналогично, если вы eta-expand свое первое определение
f x = succ x
вам больше не нужна подпись типа.
[1] Фактическое имя из документации GHC!
Ответ 2
Я имею в виду, почему мне не нужно объявлять f
как (+) :: (Num a) => a –> a –> a
?
Вам нужно это сделать, если вы вообще объявляете подпись f
. Но если вы этого не сделаете, компилятор "догадается" сам подписи – в этом случае это не все замечательно, так как оно может просто скопировать и вставить подпись (+)
. И это именно то, что он будет делать.
... или, по крайней мере, что он должен делать. Это происходит, если вы используете флаг -XNoMonomorphism
. В противном случае, страшное ограничение мономорфизма входит, потому что определение f
имеет форму ConstantApplicativeForm = Значение; что делает компилятор немой подписи к следующему лучшему неполиморфному типу, который он может найти, а именно Integer -> Integer -> Integer
. Чтобы этого не произошло, вы должны фактически предоставить правильную подпись вручную, для всех функций верхнего уровня. Это также предотвращает много путаницы, и многие ошибки становятся менее запутанными.
Ограничение мономорфизма является причиной
f = succ
не будет работать сам по себе: поскольку он также имеет эту форму CAF, компилятор не пытается вывести правильный полиморфный тип, но пытается найти конкретный экземпляр для создания мономорфной сигнатуры. Но в отличие от Num
, класс Enum
не предлагает экземпляр по умолчанию.
Возможные решения, упорядоченные по предпочтению:
- Всегда добавлять подписи. Вы действительно должны.
- Включить
-XNoMonomorphismRestriction
.
- Напишите определения функций в форме
f a = succ a
, f a b = a+b
. Поскольку явно указаны аргументы, они не квалифицируются как CAF, поэтому ограничение мономорфизма не будет пинаться.
Ответ 3
Haskell по умолчанию устанавливает ограничения Num
на Int
или Integer
, я забыл, что.