2013-03-16 3 views
12

Рассмотрим следующую функцию Haskell:(Num а) против типа вывода Integer

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 

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

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

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

Тем не менее, следующие работы просто отлично:

*Main> double (sign 5.5) 
2 

Что это такое, что я неправильно понимаю о системе типов в Haskell?

+0

Кстати, есть, конечно, также функция 'signum :: Num a => a -> a', то есть вы передаете число в нее и возвращаете число * одного и того же типа *. – mrueg

+0

Тот факт, что вы передали его 'double', дает двигателю ввода вывода больше знаний - он знает, что он работает с' double', и выясняет, как это сделать. – jpaugh

ответ

18

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

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

Но тип

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

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

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

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

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

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

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

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

+0

Ах, ваши изменения сделали мой ответ несколько избыточным ... –

+0

В некоторой степени. Тем не менее, пусть это будет стоять, хорошо иметь альтернативную формулировку. –

+0

Большое спасибо за этот ответ! Это немного объясняет мне ... Я забыл, что возвращаемый тип «Num a» - Num, где «a» может быть любым (при условии, что существует конструктор типа «Num a»). –

6

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

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

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

10

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

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

+0

Чтобы сделать это более точно (но менее читаемо): 'f' способен возвращать любое значение, тип которого является экземпляром' Num'. Контекст вызова 'f' используется для определения конкретного _type_' b'; возвращаемое _value_ зависит, конечно, от 'f' и входных данных. –

Смежные вопросы