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

Haskell: класс типа YesNo. Почему Integer?

У меня вопрос о том, как GHCi принимает тип целого числа.

Я читал класс типа "Нет-нет". Узнайте, что вы Haskell.

Вот ссылка, если вы хотите прочитать все это. http://learnyouahaskell.com/making-our-own-types-and-typeclasses#a-yes-no-typeclass

Вкратце, в этой главе показано, что, определяя мой собственный класс, я могу создать функцию, которая работает с большим количеством типов.

В этой книге определяется класс YesNo с функцией

yesno :: a -> Bool

и сделайте Int в качестве примера класса YesNo

instance YesNo Int where
    yesno 0 = False
    yesno _ = True

Когда я загрузил это на свой GHCi и набрал

yesno 0

он возвратил ошибку. Я думал, что это, вероятно, потому, что GHCi не может определить, должно ли 0 быть Int или Integer или Double или другим типом в классе Num. Фактически, когда я набрал yesno (0:: Int), он сработал.

Итак, просто для удовольствия я сделал Integer как экземпляр класса YesNo и написал

instance YesNo Integer where
    yesno 0 = True
    yesno _ = False

(Обратите внимание, что я перевернул True и False) и снова я набрал

yesno 0

(без объявления какого-либо типа), то GHCi показал True.

Кроме того, когда я набрал

yesno $ fromIntegral 0

он возвратил True, что означает, что GHCi считает, что тип fromIntegral 0 равен Integer.

Итак, значит ли это, что когда я просто набираю целое число на GHCi, обычно он принимает значение Integer вместо? Я запутался, потому что :t 0 возвращает Num a => a

4b9b3361

Ответ 1

Введите defaulting вместе с расширенными правилами по умолчанию ghci.

Целочисленные литеры являются полиморфными, они имеют тип Num a => a (поскольку они обозначают fromInteger literal). Но когда выражение должно быть оценено - необходимое для печати его результата, например - выражение должно быть задано мономорфным типом.

Сам по себе

yesno 0

накладывает два ограничения Num a и YesNo a на 0, и все выражение будет иметь двусмысленный тип

yesno 0 :: (Num a, YesNo a) => Bool

(это неоднозначно, поскольку переменная типа в ограничении недоступна от типа справа от =>).

Как правило, неоднозначные типы являются ошибками типа, однако в некоторых случаях неоднозначность разрешается путем создания экземпляра переменной ограничения с типом по умолчанию. Правила в спецификации языка заключаются в том, что переменная типа может быть установлена ​​по умолчанию, если

В ситуациях, когда обнаружен двусмысленный тип, переменная неоднозначного типа v не применяется, если:

- `v` appears only in constraints of the form `C v`, where `C` is a class, and
- at least one of these classes is a numeric class, (that is, `Num` or a subclass of `Num`), and
- all of these classes are defined in the Prelude or a standard library (Figures 6.2–6.3 show the numeric classes, and Figure 6.1 shows the classes defined in the Prelude.)

Ограничение (Num a, YesNo a) соответствует первым двум требованиям, но не третьему. Таким образом, по языковому стандарту он не является недействительным и должен быть ошибкой типа.

Однако ghci использует расширенные правила по умолчанию, а также типы переменных по умолчанию, ограниченные классами, не определенными в прелюдии или стандартных библиотеках.

Затем он будет выбирать значение по умолчанию для ограничения Num, если только явная декларация по умолчанию в области видимости не будет Integer, или если Integer не удовлетворяет ограничениям, проверяется Double.

Поэтому, когда у вас есть instance YesNo Integer, ghci может успешно использовать переменную типа a до Integer. Но, если такой экземпляр недоступен, дефолт не выполняется, потому что ни один из кандидатов по умолчанию не имеет экземпляра.

Ответ 2

Итак, означает ли это, что когда я просто набираю целое число на GHCi, обычно он принимает значение Integer?

Да. В принципе, GHCi сначала попробует Integer, а затем, если это не сработает, Double, а затем, наконец, (), чтобы устранить двусмысленные ограничения типа. Вы можете прочитать сведения о том, как это работает в Руководстве пользователя GHC.

Однако обратите внимание, что в скомпилированных модулях правила немного более строгие. В частности, дефолт применяется только к стандартным классам, поэтому ваш пример не будет работать без аннотации типа в скомпилированном модуле, если вы не включите расширение ExtendedDefaultRules, которое дает вам такое же поведение, что и GHCi.

Ответ 3

в первом случае попробуйте написать:

Prelude> yesno (0 :: Int)
False