Выбрать главу

circumference' :: Double –> Double

circumference' r = 2 * pi * r

Загрузите дополненный файл и запустите новую функцию

ghci> circumference' 4.0

25.132741228718345

• Тип Bool – булевский. Он может принимать только два значения: True и False.

• Тип Char представляет символ Unicode. Его значения записываются в одинарных кавычках. Список символов является строкой.

• Кортежи – это типы, но тип кортежа зависит от его длины и от типа его компонентов. Так что теоретически количество типов кортежей бесконечно – а стало быть, перечислить их все в этой книге нет возможности. Заметьте, что пустой кортеж () – это тоже тип, который может содержать единственное значение: ().

Типовые переменные

Некоторые функции могут работать с данными разных типов. Например, функция head принимает список и возвращает его первый элемент. При этом неважно, что именно этот список содержит – числа, символы или вообще другие списки. Функция должна работать со списками, что бы они ни содержали.

Как вы думаете, каков тип функции head? Проверим, воспользовавшись командой :t.

ghci> :t head

head :: [a] –> a

Гм-м! Что такое a? Тип ли это? Мы уже отмечали, что все типы пишутся с большой буквы, так что это точно не может быть типом. В действительности это типовая переменная. Иначе говоря, a может быть любым типом.

Подобные элементы напоминают «дженерики» в других языках – но только в Haskell они гораздо более мощные, так как позволяют нам легко писать самые общие функции (конечно, если эти функции не используют какие-нибудь специальные свойства конкретных типов).

Функции, в объявлении которых встречаются переменные типа, называются полиморфными. Объявление типа функции head выше означает, что она принимает список любого типа и возвращает один элемент того же типа.

ПРИМЕЧАНИЕ. Несмотря на то что переменные типа могут иметь имена, состоящие более чем из одной буквы, мы обычно называем их a, b, c, d…

Помните функцию fst? Она возвращает первый компонент в паре. Проверим её тип:

ghci> :t fst

fst :: (a, b) –> a

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

Заметьте, что хотя a и b – различные переменные типа, они вовсе не обязаны быть разного типа. Сигнатура функции fst лишь означает, что тип первого компонента и тип возвращаемого значения одинаковы.

Классы типов

Класс типов – интерфейс, определяющий некоторое поведение. Если тип является экземпляром класса типов, то он поддерживает и реализует поведение, описанное классом типов. Более точно можно сказать, что класс типов определяет набор функций, и если мы решаем сделать тип экземпляром класса типов, то должны явно указать, что эти функции означают применительно к нашему типу.

Хорошим примером будет класс типов, определяющий равенство. Значения многих типов можно сравнивать на равенство с помощью оператора ==. Посмотрим на его сигнатуру:

ghci> :t (==)

(==) :: (Eq a) => a –> a –> Bool

Заметьте: оператор равенства == – это функция. Функциями также являются операторы +, *, , / и почти все остальные операторы. Если имя функции содержит только специальные символы, по умолчанию подразумевается, что это инфиксная функция. Если мы захотим проверить её тип, передать её другой функции или вызвать как префиксную функцию, мы должны поместить её в круглые скобки.

Интересно… мы видим здесь что-то новое, а именно символ =>. Всё, что находится перед символом =>, называется ограничением класса. Мы можем прочитать предыдущее объявление типа следующим образом: «функция сравнения на равенство принимает два значения одинакового типа и возвращает значение типа Bool. Тип этих двух значений должен быть экземпляром класса Eq» (это и есть ограничение класса).

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