На главную Назад Вперёд

Краткая сводка синтаксиса

В этой главе дана краткая справка по наиболее часто используемым конструкциям языка Haskell.

Общая структура программы

определение_модуля

список_импортируемых_названий


определение_1

определение_2

...

определение_n

Все определения начинаются в одной и той же колонке (обычно первой).

С отступами принята следующая политика: все части одного блока обязаны начинаться в одной и той же колонке. Часть блока, не помещающаяся в одну строчку, должна находиться строго правее начальной колонки блока. Блок начинается только одним из следующих слов: do, where, of, let.

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

Однострочные комментарии выделяются парой минусов. Многострочные – сочетаниями {- и -}.

Определение модуля

Обычно

module Foo where

Название модуля обязано начинаться с большой буквы. Файл с модулем Foo должен называться Foo.hs.

Определение модуля можно опустить. В этом случае считается, что написано

module Main where

В модуле Main должна быть определена константа main типа IO ().

В программах, состоящих из нескольких модулей в определение модуля ещё может входить информация об экспортируемых названиях. Для наших целей это пока неактуально.

Импорт названий

Если хочется использовать вещи, определённые в модуле Foo, обычно используется один из четырёх следующих вариантов импортирования:

import Foo                  -- импортируются все названия 
import Foo(foo,bar,baz)     -- импортируются только foo, bar и baz
import qualified Foo        -- импортируются все названия, но к каждому добавляется Foo. спереди
import qualified Foo as Bar -- импортируются все названия, но к каждому добавляется Bar. спереди

Определения

Есть следующие разновидности определений:

type Foo параметры = тип  -- определение синонима существующего типа данных

data Foo параметры = конструктор_1 | конструктор_2 | ... | конструктор_n  -- определение нового типа данных

newtype Foo параметры = название_конструктора тип  -- определение обёртки вокруг существующего типа данных

class Foo параметр where блок  -- определение класса типов

instance Foo Bar where  -- добавление типа Bar к классу Foo

foo, bar, baz :: тип  -- указание типа объектов foo, bar, baz

foo параметры = формула  -- определение функции foo

шаблон = выражение  -- определение объектов при помощи сопоставления с образцом

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

Встроенные типы данных

К базовым типам данных относятся Char, Int, Integer, Float, Double и несколько других разновидностей числовых типов данных.

Для удобства в Haskell есть специальный синтаксис для некоторых распространённых сложных типов данных:

-- кортежи (гетерогенные последовательности фиксированной длины)
data ()        = ()
data (a,b)     = (,)   a b
data (a,b,c)   = (,,)  a b c
data (a,b,c,d) = (,,,) a b c d
...

-- для (,) x y есть сокращение (x,y); аналогичные сокращения есть и для остальных кортежей


-- списки (гомогенные последовательности произвольной длины)
data [x] = [] | (:) x [x]  -- оператор (:) правоассоциативен и имеет приоритет 5

-- для a_1 : a_2 : ... : a_n : [] есть сокращение [a_1, a_2, ... a_n]

Сложные выражения

Есть следующие сложные выражения: let-блок, do-блок, case-блок, if-then-else-выражение, лямбда-выражение.

Их синтаксис таков:

let
  определение_1
  определение_2
  ...
  определение_n
in выражение


case выражение of
  шаблон_1 -> выражение_1
  шаблон_2 -> выражение_2
  ...
  шаблон_n -> выражение_n
  

if выражение then выражение else выражение


\параметры -> выражение


do
  команда_1
  команда_2
  ...
  команда_n

В рамках do-блока допускаются следующие три вида команд:

выражение типа m x

шаблон типа x <- выражение типа m x

let блок_определений (без всякого in)

Во всех командах одного do-блока тип m должен быть одним и тем же; также тип m должен принадлежать классу Monad.

Стоит отметить, что фундаментальными являются только case-выражения и лямбда-выражения с одним нешаблонным параметром. Всё остальное является сокращением для часто используемых комбинаций этих двух типов выражений (иногда весьма сложно устроенных; типичный пример – взаимно рекурсивные определения внутри let-блока; выражение без let требует нетривиального применения комбинаторов неподвижной точки).

@ 2016 arbrk1, all rights reversed