В этой главе дана краткая справка по наиболее часто используемым конструкциям языка 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