True
=> False
inv False
+ False
=> (not False) + False
=> True
+ False
=> and True
False
=> False
Проверять свойства очень важно, потому что другие люди, читая ваш код и используя ваши функции,
будут на них рассчитывать.
20 | Глава 1: Основы
1.6 Ядро Haskell
Фуууухх. Мы закончили наш пробег. Теперь можно остановиться, отдышаться и подвести итоги. Давайте
вспомним синтаксические конструкции, которые нам встретились.
Модули
module New(edef1, edef2, ... , edefN) where
import Old1(idef11, idef12, ... , idef1N)
import Old2(idef21, idef22, ... , idef2M)
...
import OldK(idefK1, idefK2, ... , idefKP)
-- определения :
...
Ключевые слова: module, where, import. Мы определили модуль с именем New, который экспортирует
определения edef1, edef2, … , edefN. И импортирует определения из модулей Old1, Old2, и т.д., определения
написаны в скобках за ключевыми словами import и именами модулей.
Типы
Тип определяется с помощью:
• Перечисления альтернатив через |
data Type = Alt1 | Alt2 | ... | AltN
Эту операцию называют
• Составления сложного типа из подтипов, пишем конструктор первым, затем через пробел подтипы:
data Type = Name
Sub1
Sub2
...
SubN
Эту операцию называют
Есть одно исключение: если тип состоит из двух подтипов, мы можем дать конструктору символьное
(а не буквенное) имя, но оно должно начинаться с двоеточия :, как в случае списка, например, можно
делать такие определения типов:
data Type = Sub1 :+ Sub2
data Type = Sub1 :| Sub2
• Комбинации суммы и произведения типов:
data Type = Name1
Sub11
Sub12
...
Sub1N
| Name2
Sub21
Sub22
...
Sub2M
...
| NameK
SubK1
SubK2
...
SubKP
Такие типы называют
тия и способы их комбинирования.
Значения
Как это ни странно, нам встретилась лишь одна операция создания значений:
пишется так
name x1
x2 ... xN = Expr1
name x1
x2 ... xN = Expr2
name x1
x2 ... xN = Expr3
Слева от знака равно стоит составное имя, а справа от знака равно некоторое выражение, построенное
согласно типам. Разные комбинации имени name с параметрами определяют разные уравнения для синонима
name.
Также мы видели символ _, который означает “всё, что угодно” на месте аргумента. А также мы увидели,
как с помощью переменных можно перетаскивать значения из аргументов в результат.
Ядро Haskell | 21
Классы типов
Нам встретилась одна конструкция определения классов типов:
class Name a where
method1 :: a -> ...
method2 :: a -> ...
...
methodN :: a -> ...
Экземпляры классов типов
Нам встретилась одна конструкция определения экземпляров классов типов:
instance Name Type where
method1 x1 ... xN = ...
method2 x1 ... xM = ...
...
methodN x1 ... xP = ...
Типы, значения и классы типов
Каждое значение имеет тип. Значение v имеет тип T на Haskell:
v :: T
Функциональный тип обозначается стрелкой: a -> b
fun :: a -> b
Тип значения может иметь контекст, он говорит о том, что параметр должен принадлежать классу типов:
fun1 :: С a
=> a -> a
fun2 :: (C1 a, C2, ... , CN) => a -> a
Суперклассы
Также контекст может быть и у классов, запись
class A a => B a where
...
Означает, что класс B целиком содержится в A, и перед тем как объявлять экземпляр для класса B, необ-
ходимо определить экземпляр для класса A. При этом класс A называют суперклассом для B.
1.7 Двумерный синтаксис
Наверное вы обратили внимание на то, что в Haskell нет разделителей строк и дополнительных скобок,
которые бы указывали границы определения классов или функций. Компилятор Haskell ориентируется по
переносам строки и отступам.
Так если мы пишем в классе:
class Eq a where
(==) :: a -> a -> a
(/=) :: a -> a -> a
По отступам за первой строкой определения компилятор понимает, что класс содержит два метода. Если
бы мы написали:
class Eq a where
(==) :: a -> a -> a
(/=) :: a -> a -> a
22 | Глава 1: Основы
То смысл был бы совсем другим. Теперь мы определяем класс Eq с одним методом == и указываем тип
некоторого значения (/=). Основное правило такое: конструкции, расположенные на одном уровне, вырав-
ниваются с помощью отступов. Чем правее находится определение, тем глубже оно вложено в какую-нибудь
специальную конструкцию. Пока нам встретилось лишь несколько специальных конструкций, но дальше
появятся и другие. Часто отступы набираются с помощью табуляции. Это удобно. Но лучше пользоваться
пробелами или настроить ваш любимый текстовый редактор так, чтобы он автоматически заменял табуля-