Да, тип succ i
выведен так, как вы ожидаете:
Prelude> :t succ i
succ i :: (Enum a, Num a) => a
Этот тип неоднозначен, но он удовлетворяет условиям в правилах по умолчанию для GHCi:
Найти все нерешенные ограничения. Тогда:
- Найдите те, которые имеют форму
(C a)
, где a
- переменная типа, и разбейте эти ограничения на группы, которые имеют общую переменную типа a
.
В этом случае есть только одна группа: (Enum a, Num a)
.
- Оставьте только те группы, в которых хотя бы один из классов является интерактивным (определено ниже).
Эта группа сохраняется, потому что Num
- это интерактивный класс.
Теперь для каждой оставшейся группы G по очереди попробуйте каждый тип ty
из списка типов по умолчанию; если установка a = ty
позволит полностью разрешить ограничения в G. Если это так, по умолчанию от a
до ty
.
Тип блока ()
и тип списка []
добавляются в начало стандартного списка типов, которые используются при выполнении значений по умолчанию.
Список типов по умолчанию (sic) по умолчанию (с дополнениями из последнего предложения) default ((), [], Integer, Double)
.
Таким образом, когда вы делаете Prelude> succ i
для фактической оценки этого выражения (примечание :t
не оценивает полученное выражение), a
устанавливается на Integer
(первый из этого списка удовлетворяет ограничениям), и результат печатается как 2
.
Вы можете увидеть, в чем причина, изменив значение по умолчанию:
Prelude> default (Double)
Prelude> succ 1
2.0
Для обновленного вопроса:
Теперь я начинаю осознавать, что мой вопрос должен исходить из неверного предположения: «если i
выведено как i :: Num a => a
, то i
не может быть ничем иным». Поэтому я был озадачен, увидев, что succ i
был оценен без ошибок.
i
может быть ничем иначе (то есть ничем, что не соответствует этому типу), но его можно использовать с менее общими (более конкретными) типами: Integer
, Int
. Даже со многими из них в выражении сразу:
Prelude> (i :: Double) ^ (i :: Integer)
1.0
И это не влияет на сам тип i
: он уже определен, а его тип фиксирован. Хорошо, пока?
Что ж, добавление ограничений также делает тип более конкретным, поэтому (Num a, Enum a) => a
более специфично, чем (Num a) => a
:
Prelude> i :: (Num a, Enum a) => a
1
Поскольку, конечно, любой тип a
, который удовлетворяет обоим ограничениям в (Num a, Enum a)
, удовлетворяет только Num a
.
Однако он не добавляет Enum a
, когда переменная типа отображается как функция:
Это потому, что вы указали подпись, которая не позволяет этого. Если вы не ставите подпись, нет никаких оснований выводить ограничение Num
. Но, например,
Prelude> f x = succ x + 1
выведет тип с обоими ограничениями:
Prelude> :t f
f :: (Num a, Enum a) => a -> a
Так что кажется, что правила вывода для функций более строгие, чем правила для переменных?
На самом деле все наоборот из-за ограничения мономорфизма (по умолчанию не в GHCi). Тебе действительно повезло, что ты не столкнулся с этим здесь, но ответ уже достаточно длинный. Поиск термина должен дать вам объяснения.
GHC говорит my_succ :: forall a. Num a => a -> a
, а данный forall a
не появляется в сигнатуре типа ни i
, ни x
.
Это красная сельдь. Я не уверен, почему это показано в одном случае, а не в другом, но у всех них есть это forall a
за кадром:
Сигнатуры типа Haskell неявно определяются количественно. Когда используется языковая опция ExplicitForAll
, ключевое слово forall
позволяет нам точно сказать, что это значит. Например:
g :: b -> b
означает это:
g :: forall b. (b -> b)
(Кроме того, вам нужно ExplicitForAll
, а не RankNTypes
, чтобы записать forall a. Num a => a
.)