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

(Num a) vs Вывод целочисленного типа

Рассмотрим следующую функцию Haskell:

sign a
  | a < 0 = (-1)
  | a > 0 = 1
  | otherwise = 0

Когда я загружаю это в ghci, я ожидал, что :t sign будет:

sign :: (Num a, Ord a) => a -> Integer

Вместо этого он сделал вывод, что:

*Main> :t sign
sign :: (Num a1, Num a, Ord a1) => a1 -> a

Аналогично, если я задаю тип целого 5, я ожидал Integer, но вместо этого я получил

*Main> :t 5
5 :: Num a => a

Там что-то я не понимаю о типах Haskell. Дело в том, что если все, что я знаю о типе return sign, это то, что он является экземпляром класса Num, тогда я не смог бы передать его возвращаемое значение в эту функцию:

double :: Integer -> Integer
double x = x * 2

То есть для моей функции double требуется Integer, а не только любой экземпляр Num.

Тем не менее, следующее работает очень хорошо:

*Main> double (sign 5.5)
2

Что я плохо понимаю о системе типа Haskell?

4b9b3361

Ответ 1

Дело в том, что если все, что я знаю о возвращаемом типе "знака", это то, что он является экземпляром класса Num, тогда я не смог бы передать его возвращаемое значение в эту функцию:

Правильно, если бы это было все, что вы знали, вы не смогли бы передать его на double.

Но тип

sign :: (Num a1, Num a, Ord a1) => a1 -> a

означает, что тип результата sign зависит от того, какой тип Num задает запрос вызывающего абонента. Типовые переменные в типах подписей (неявно) универсально количественно, а не экзистенциально, например, например. Интерфейсы Java.

sign может выдавать возвращаемое значение произвольного типа, при условии, что это ограничение является экземпляром Num, а возвращаемый им тип определяется вызывающим контекстом.

Если вызывающий абонент хочет Integer, он получает его. Если он хочет double, не проблема.

Я забыл упомянуть изначально:

Аналогично, если я попрошу тип целого числа 5, я ожидал "Integer", но вместо этого получил

    *Main> :t 5
    5 :: Num a => a

Числовые литералы являются полиморфными, целочисленный литерал обозначает fromInteger value и дробный литерал для fromRational value.

Ответ 2

Я просто хотел немного разъяснить @DanielFischer ответ. Подпись типа f :: Num b => a -> b означает, что f способен возвращать любой экземпляр класса Num. Когда вызывается f, Haskell использует контекст (подпись типа вызывающего) для определения конкретного типа b.

Более того, числовые литералы Haskell являются примером такого типа полиморфизма. Вот почему :t 5 дал вам Num a => a. Символ 5 способен действовать как любой тип числа, а не только целое число. Контекст, в котором он появляется, определяет, какой он будет.

Ответ 3

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

Ваш sign может возвращать любые типы данных, включая Integer. Функция double требует Integer, так что просто отлично - sign может вернуть это.

Еще одна часть загадки, о которой вы не можете знать: на Java 2 имеет тип int и 2.0 имеет тип double. Но в Haskell 2 имеет тип Num x => x - другими словами, любой возможный тип номера. (Также 2.0 имеет тип Fractional x => x, что является аналогичной сделкой.)