Теперь подумаем, как бы мы представили некую геометрическую фигуру в языке Haskell. Один из способов – использовать кортежи. Круг может быть представлен как >(43.1, 55.0, 10.4)
, где первое и второе поле – координаты центра, а третье – радиус. Вроде бы подходит, но такой же кортеж может представлять вектор в трёхмерном пространстве или что-нибудь ещё. Лучше было бы определить свой собственный тип для фигуры. Скажем, наша фигура может быть кругом или прямоугольником.
>data Shape = Circle Float Float Float | Rectangle Float Float Float Float
Ну и что это? Размышляйте следующим образом. Конструктор для значения >Circle
содержит три поля типа >Float
. Когда мы записываем конструктор значения типа, опционально мы можем добавлять типы после имени конструктора; эти типы определяют, какие значения будет содержать тип с данным конструктором. В нашем случае первые два числа – это координаты центра, третье число – радиус. Конструктор для значения >Rectangle
имеет четыре поля, которые также являются числами с плавающей точкой. Первые два числа – это координаты верхнего левого угла, вторые два числа – координаты нижнего правого угла.
Когда я говорю «поля», то подразумеваю «параметры». Конструкторы данных на самом деле являются функциями, только эти функции возвращают значения типа данных. Давайте посмотрим на сигнатуры для наших двух конструкторов:
>ghci> :t Circle
>Circle :: Float –> Float –> Float –> Shape
>ghci> :t Rectangle
>Rectangle :: Float –> Float –> Float –> Float –> Shape
Классно, конструкторы значений – такие же функции, как любые другие! Кто бы мог подумать!..
Давайте напишем функцию, которая принимает фигуру и возвращает площадь её поверхности:
>area :: Shape –> Float
>area (Circle _ _ r) = pi * r ^ 2
>area (Rectangle x1 y1 x2 y2) = (abs $ x2 – x1) * (abs $ y2 – y1)
Первая примечательная вещь в объявлении – это декларация типа. Она говорит, что функция принимает фигуру и возвращает значение типа >Float
. Мы не смогли бы записать функцию типа >Circle –> Float
, потому что идентификатор >Circle
не является типом; типом является идентификатор >Shape
. По той же самой причине мы не смогли бы написать функцию с типом >True –> Int
. Вторая примечательная вещь – мы можем выполнять сопоставление с образцом по конструкторам. Мы уже записывали подобные сопоставления раньше (притом очень часто), когда сопоставляли со значениями