52
квадратными скобками (получается, что параметр этого типа a
располагается между скобками). В качестве конструктора пустого списка
использовано литеральное обозначение [], которое в действительности,
как и в случае с другими литералами, нельзя использовать в качестве
имени конструктора. Другой конструктор (тот, для которого мы ранее
выбрали идентификатор Cons) теперь обозначается двуместным
оператором
':'. Надо заметить, что на этот раз синтаксические правила
все же соблюдены, потому что как для имен функций, так и для имен
конструкторов разрешено использовать двуместные операторы,
образованные последовательностями литер из некоторого
предопределенного списка символов, в который, в частности, входит и
символ двоеточия. Чтобы обеспечить визуальную различимость
обозначений функциональных операций и
операций-конструкторов,
принято соглашение, согласно которому названия операций-конструкторов
должны начинаться с двоеточия, а названия функциональных операций,
напротив, с символа двоеточия начинаться не могут. Это соглашение
аналогично тому, что идентификаторы функций должны начинаться со
строчных букв, а идентификаторы конструкторов – с прописных. Кстати, в
вышеприведенном определении «стандартных» списков это соглашение
выполняется:
конструктор для формирования списков действительно
имеет обозначение, начинающееся с двоеточия, точнее, состоит только из
двоеточия.
Вообще, конструкторы и функции имеют много общих черт. И
конструктор, и функция служат для образования новых значений из
имеющихся (конструкторы без аргументов, скорее, напоминают не
функции, а значения-константы, заданные литерально). Разница состоит в
том, что
функции для образования нового значения требуются правила, с
помощью которых это значение и образуется, а конструктору никаких
правил не нужно – аргументы собираются конструктором в единый объект
нового типа чисто механически. Позже мы увидим, что такое сходство
конструкторов и функций позволяет в ряде случаев использовать и то и
другое абсолютно одинаковым
образом.
В качестве чуть более сложного примера задачи обработки списков
приведем пример функции, которая сортирует заданный список элементов,
то есть располагает его элементы в порядке возрастания. Это достаточно
традиционная для программирования задача, однако отметим две ее
особенности при решении ее в функциональном стиле. Во-первых, задачу
можно поставить и решить для
любых списков, элементы которых можно
сравнивать друг с другом по величине, то есть если тип этих элементов
принадлежит классу Ord. Получившаяся функция будет похожа по своей
общности на шаблон функции, который можно описать, например в языке
Си++. В языке Haskell это будет обычной (полиморфной) функцией. Во-
вторых, при использовании традиционных
языков обычно полагают, что