За исключением let -выражений эти трансляции сохраняют и статическую, и динамическую семантику. Prelude. Например, "concatMap", используемая в трансляции описания списка (раздел 3.11), обозначает concatMap, определенную в Prelude, невзирая на то, находится ли идентификатор "concatMap" в области видимости, где используется описание списка, и (если находится в области видимости) к чему он привязан.
В синтаксисе, который следует далее, есть некоторые семейства нетерминалов, индексированные уровнями приоритета (записанными как верхний индекс). Аналогично, нетерминалы (оператор), varop (оператор-переменной) и conop (оператор-конструктора) могут иметь двойной индекс: букву l, r или n соответственно для левоассоциативности, правоассоциативности или отсутствия ассоциативности и уровень приоритета. Переменная уровня приоритета i изменяется в пределах от 0 до 9, переменная ассоциативности a изменяется в диапазоне {l, r, n}. Например,
aexp |
-> |
( expi+1 qop(a,i) ) |
на самом деле обозначает 30 правил вывода с 10 подстановками для i и 3 для a.
| exp | -> |
exp0 :: [context =>] type |
(сигнатура типа выражения) |
| | | exp0 |
||
expi |
-> |
expi+1 [qop(n,i) expi+1] |
|
| | | lexpi |
||
| | | rexpi |
||
lexpi |
-> |
(lexpi | expi+1) qop(l,i) expi+1 |
|
lexp6 |
-> |
- exp7 |
|
rexpi |
-> |
expi+1 qop(r,i) (rexpi | expi+1) |
|
exp10 |
-> |
\ apat_1 ... apat_n -> exp |
(лямбда-абстракция, n>= 1 ) |
| | | let decls in exp |
(let-выражение) | |
| | | if exp then exp else exp |
(условное выражение) | |
| | | case exp of { alts } |
( |
|
| | | do { stmts } |
(do-выражение) | |
| | | fexp |
||
fexp |
-> |
[fexp] aexp |
(применение функции) |
aexp |
-> |
qvar |
(переменная) |
| | | gcon |
(общий конструктор) | |
| | | |
||
| | | ( exp ) |
(выражение в скобках) | |
| | | ( exp1 , ... , expk ) |
(кортеж, k>= 2 ) |
|
| | | [ exp1 , ... , expk ] |
(список, k>= 1 ) |
|
| | | [ exp1 [, exp2] .. [exp3] ] |
(арифметическая последовательность) | |
| | | [ exp | qual1 , ... , qualn ] |
(описание списка, n>= 1 ) |
|
| | | ( expi+1 qop(a,i) ) |
(левое сечение) | |
| | | ( lexpi qop(l,i) ) |
(левое сечение) | |
| | | ( qop(a,i)<-> expi+1 ) |
(правое сечение) | |
| | | ( qop(r,i)<-> rexpi ) |
(правое сечение) | |
| | | qcon { fbind1 , ... , fbindn } |
(именованная конструкция, n>= 0) |
|
| | | aexp<qcon> { fbind1 , ... , fbindn } |
(именованное обновление, n >= 1) |
Перевод:
| выражение | -> -> |
выражение^0 :: [контекст => ] тип |
(сигнатура типа выражения) |
| | | выражение0 | ||
| выражениеi | -> |
выражениеi+1 [квалифицированный-оператор(n,i) выражениеi+1] | |
| | | левое-сечение-выраженияi | ||
| | | правое-сечение-выраженияi | ||
| левое-сечение-выраженияi | -> |
(левое-сечение-выраженияi | выражениеi+1) квалифицированный-оператор(l,i) выражениеi+1 | |
| левое-сечение-выражения6 | -> |
- выражение7 | |
| правое-сечение-выраженияi | -> |
выражениеi+1 квалифицированный-оператор(r,i) (правое-сечение-выраженияi | выражениеi+1) | |
| выражение10 | -> |
\ такой-как-образец1 ... такой-как-образецn -> выражение |
(лямбда-абстракция, n>= 1) |
| | | let списки-объявлений in выражение |
( let -выражение) |
|
| | | if выражение then выражение else выражение |
(условное выражение) | |
| | | case выражение of { список-альтернатив } |
( case -выражение) |
|
| | | do { список-инструкций } |
( do -выражение) |
|
| | | функциональное-выражение | ||
| функциональное-выражение | -> |
[функциональное-выражение] выражение-аргумента | (применение функции) |
| выражение-аргумента | -> |
квалифицированная-переменная | (переменная) |
| | | общий-конструктор | (общий конструктор) | |
| | | литерал | (выражение в скобках) | |
| | | ( выражение ) | ||
| | | ( выражение1 , ... , выражениеk ) | (кортеж, k>= 2) |
|
| | | [ выражение1 , ... , выражениеk ] | (список, k >= 1) |
|
| | | [ выражение1 [, выражение2] .. [выражение3] ] | (арифметическая последовательность) | |
| | | [ выражение | квалификатор1 , ... , квалификаторn ] | (описание списка, n >= 1) |
|
| | | ( выражениеi+1 квалифицированный-оператор(a,i) ) | (левое сечение) | |
| | | ( левое-сечение-выраженияi квалифицированный-оператор(l,i) ) | (левое сечение) | |
| | | ( квалифицированный-оператор(a,i)<-> выражениеi+1 ) | (правое сечение) | |
| | | ( квалифицированный-оператор(r,i)<-> правое-сечение-выраженияi ) | (правое сечение) | |
| | | квалифицированный-конструктор { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованная конструкция, n>=0) |
|
| | | выражение-аргумента<квалифицированный-конструктор> { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованное обновление, n>=1) |
Неоднозначность выражений, включая инфиксные операторы, разрешается с помощью ассоциативности и приоритета оператора (см. раздел 4.4.2). Следующие друг за другом операторы без скобок, имеющие один и тот же приоритет, должны быть оба либо левоассоциативными, либо правоассоциативными, во избежание синтаксической ошибки. Для заданного выражения без скобок "x qop(a,i) y qop(b,j) z", где qop - оператор, часть "x qop(a,i) y" или "y qop(b,j) z" следует взять в скобки, когда i=j, за исключением a=b=l или a=b=r.
Отрицание является единственным -, определенный в Prelude (см. раздел 4.4.2).
Грамматика является неоднозначной по отношению к пространству лямбда-абстракций, let-выражений и условных выражений. Неоднозначность разрешается с помощью мета-правила, согласно которому каждая из этих конструкций рассматривается слева направо насколько это возможно.
Примеры интерпретации при разборе показаны ниже.
| Это | интерпретируется как |
|---|---|
f x + g y |
(f x) + (g y) |
- f x + y |
(- (f x)) + y |
let { ... } in x + y |
let { ... } in (x + y) |
z + let { ... } in x + y |
z + (let { ... } in (x + y)) |
f x y :: Int |
(f x y) :: Int |
\ x -> a+b :: Int |
\ x -> ((a+b) :: Int) |
Замечание относительно разбора. Выражения, которые затрагивают взаимодействие ассоциативности и приоритетов с применением мета-правила для let/лямбда, могут оказаться трудными для разбора. Например, выражение
let x = True in x == x == True
не может означать
let x = True in (x == x == True)
потому что (==) является неассоциативным оператором, поэтому выражение должно быть интерпретировано таким образом:
(let x = True in (x == x)) == True
Тем не менее, реализации могут прекрасно обойтись дополнительным проходом после завершения разбора, чтобы обработать ассоциативность и приоритеты операторов, поэтому они могут спокойно передать предыдущий неправильный разбор. Мы советуем программистам избегать использования конструкций, чей разбор затрагивает взаимодействие (отсутствия) ассоциативности с применением мета-правила для let/лямбда.
Ради ясности остальная часть этого раздела описывает синтаксис выражений без указания их приоритетов.
Ошибки во время вычисления выражений, обозначаемые как _|_, в программе на _|_. Другими словами, значение любого типа может быть связано с вычислением, которое будет завершено, когда потребуется результат вычисления, и завершится с ошибкой. При своем вычислении ошибки вызывают немедленное завершение программы и не могут быть отловлены пользователем. Prelude содержит две функции, которые сразу вызывают такие ошибки:
error :: String -> a undefined :: a
Вызов функции error завершает выполнение программы и возвращает в операционную систему соответствующий признак ошибки. Он также должен вывести на экран строку некоторым, зависящим от системы, способом. Когда используется функция undefined, сообщение об ошибке создается компилятором.
Трансляции выражений error и undefined для явного указания мест, где могли возникнуть ошибки времени выполнения. Реальное поведение программы в случае возникновения ошибки зависит от реализации. Сообщения, передаваемые в функцию error при этих трансляциях, являются лишь указаниями, реализации могут выбирать между отображением большей или меньшей информации в случае возникновения ошибки.
aexp |
-> |
qvar |
(переменная) |
| | | gcon |
||
| | | |
(общий конструктор) | |
gcon |
-> |
() | |
| | | [] | ||
| | | (,{,}) | ||
| | | qcon |
||
var |
-> |
varid | ( varsym ) |
(переменная) |
qvar |
-> |
qvarid | ( qvarsym ) |
(квалифицированная переменная) |
con |
-> |
conid | ( consym ) |
(конструктор) |
qcon |
-> |
qconid | ( gconsym ) |
(квалифицированный конструктор) |
varop |
-> |
varsym | 'varid ' |
(оператор переменной) |
qvarop |
-> |
qvarsym | 'qvarid ' |
(квалифицированный оператор переменной) |
conop |
-> |
consym | 'conid ' |
(оператор конструктора) |
qconop |
-> |
gconsym | 'qconid ' |
(квалифицированный оператор конструктора) |
|
-> |
varop | conop |
(оператор) |
qop |
-> |
qvarop | qconop |
(квалифицированный оператор) |
gconsym |
-> |
: | qconsym |
Перевод:
| выражение-аргумента | -> |
квалифицированная-переменная | (переменная) |
| | | общий-конструктор | (общий конструктор) | |
| | | литерал | ||
| общий-конструктор | -> |
() | |
| | | [] | ||
| | | (,{,}) | ||
| | | квалифицированный-конструктор | ||
| переменная | -> |
идентификатор-переменной | ( символ-переменной ) | (переменная) |
| квалифицированная-переменная | -> |
квалифицированный-идентификатор-переменной | ( квалифицированный-символ-переменной ) | (квалифицированная переменная) |
| конструктор | -> |
идентификатор-конструктора | ( символ-конструктора ) | (конструктор) |
| квалифицированный-конструктор | -> |
квалифицированный-идентификатор-конструктора | ( символ-общего-конструктора ) | (квалифицированный конструктор) |
| оператор-переменной | -> |
символ-переменной | 'идентификатор-переменной ' | (оператор переменной) |
| квалифицированный-оператор-переменной | -> |
квалифицированный-символ-переменной | 'квалифицированный-идентификатор-переменной ' | (квалифицированный оператор переменной) |
| оператор-конструктора | -> |
символ-конструктора | 'идентификатор-конструктора ' | (оператор конструктора) |
| квалифицированный-оператор-конструктора | -> |
символ-общего-конструктора | 'квалифицированный-идентификатор-конструктора ' | (квалифицированный оператор конструктора) |
| оператор | -> |
оператор-переменной | оператор-конструктора | (оператор) |
| квалифицированный-оператор | -> |
квалифицированный-оператор-переменной | квалифицированный-оператор-конструктора | (квалифицированный оператор) |
| символ-общего-конструктора | -> |
: | квалифицированный-символ-конструктора |
Для поддержки инфиксной записи в
Оператор представляет собой символ оператора, например, + или $$, или обычный идентификатор, заключенный в обратные кавычки, например, '. Вместо префиксной записи можно использовать инфиксную запись x '. Если ассоциативность и приоритет для ' не заданы, то по умолчанию используется наивысший приоритет и левоассоциативность (см. раздел 4.4.2).
Наоборот, символ оператора может быть преобразован в обычный идентификатор, если записать его в круглых скобках. Например, (+) x y эквивалентно x + y, а foldr (*) 1 xs эквивалентно foldr (\x y -> x*y) 1 xs.
Для обозначения некоторых конструкторов
Целый литерал представляет собой применение функции fromInteger к соответствующему значению типа Integer. Аналогично, литерал с плавающей точкой обозначает применение функции fromRational к значению типа Rational (то есть Ratio Integer ).
Трансляция:
Целый литерал i эквивалентен fromInteger i, где fromInteger - метод класса Num (см. раздел 6.4.1).
Литерал с плавающей точкой f эквивалентен fromRational (n Ratio.% d), где fromRational - метод класса составляет из двух целых чисел Ratio. Если заданы целые числа n и d, то n/d = f.
fexp |
-> |
[fexp] aexp |
(применение функции) |
exp |
-> |
\ apat_1 ...; apat_n -> exp |
(лямбда-абстракция, n >=1 ) |
Перевод:
| функциональное-выражение | -> |
[функциональное-выражение] выражение-аргумента | (применение функции) |
| выражение | -> |
\ такой-как-образец1 ... такой-как-образецn -> выражение |
(лямбда-абстракция, n >= 1 ) |
Применение функции записывается в виде e1 e2. Применение левоассоциативно, поэтому в (f x) y скобки можно опустить. Поскольку e1 может являться конструктором данных, возможно частичное применение конструкторов данных.
Лямбда-абстракции записываются в виде \ p1 ... pn -> e, где pi - образцы. Выражение вида \x:xs -> x синтаксически неправильно, его можно правильно записать в виде
\(x:xs) -> ;x.
Набор образцов должен быть линейным: ни одна переменная не должна появляться в наборе более одного раза.
Трансляция:
Выполняются следующие
\ p1 ... pn -> e=\ x1 ... xn -> case (x1, ... , xn) of (p1, ..., pn) -> e
где xi - новые идентификаторы.
Используя эту трансляцию в комбинации с с семантикой _|_.
exp |
-> |
exp1 qop exp2 |
|
| | | - exp |
(префиксное отрицание) | |
qop |
-> |
qvarop | qconop |
(квалифицированный оператор) |
Перевод:
| выражение | -> |
выражение1 квалифицированный-оператор выражение2 | |
| | | - выражение | (префиксное отрицание) | |
| квалифицированный-оператор | -> |
квалифицированный-оператор-переменной | квалифицированный-оператор-конструктора | (квалифицированный оператор) |
Форма e1 qop e2 представляет собой инфиксное применение бинарного оператора qop к выражениям e1 и e2.
Специальная форма -e обозначает префиксное отрицание, единственный (e). , определенную в Prelude. Нет никакой связи между локальным значением оператора - и унарным отрицанием.
e1-e2 при разборе интерпретируется как инфиксное применение бинарного оператора -, для альтернативной интерпретации нужно писать e1(-e2). Аналогично, (-) является синтаксической записью (\ x y -> x-y), как и любой инфиксный оператор, и не обозначает (\ x -> -x) - для этого нужно использовать
Трансляция:
Выполняются следующие
e1
aexp |
-> |
( expi+1 qop(a,i) ) |
(левое сечение) |
| | | ( lexpi qop(l,i) ) |
(левое сечение) | |
| | | ( qop(a,i)<> expi+1 ) |
(правое сечение) | |
| | | ( qop(r,i)< -> < -> rexpi ) |
(правое сечение) |
Перевод:
| выражение-аргумента | -> |
( выражениеi+1 квалифицированный-оператор(a,i) ) | (левое сечение) |
| | | ( левое-сечение-выраженияi квалифицированный-оператор(l,i) ) | (левое сечение) | |
| | | ( квалифицированный-оператор(a,i)<-> выражениеi+1 ) | (правое сечение) | |
| | | ( квалифицированный-оператор(r,i)<-> правое-сечение-выраженияi ) | (правое сечение) |
Сечения записываются в виде ( или ( e , где - e - выражение. Сечения представляют собой удобный синтаксис для записи частичного применения
Синтаксические правила приоритетов применяются к сечениям следующим образом. ( допустимо, если и только если (x при разборе интерпретируется так же, как и (x ; аналогично для (e . Например, (*a+b) синтаксически недопустимо, но (+a*b) и (*(a+b)) допустимы. Поскольку оператор (+) левоассоциативен, (a+b+) синтаксически правильно, а (+a+b) - нет, его можно правильно записать в виде (+(a+b)). В качестве другого примера рассмотрим выражение
(let n = 10 in n +)
которое является недопустимым в соответствии с мета-правилом для let/лямбда (раздел 3). Выражение
(let n = 10 in n + x)
при разборе интерпретируется как
(let n = 10 in (n + x))
вместо
((let n = 10 in n) + x)
Поскольку - интерпретируется в грамматике специальным образом, (- exp) является не сечением, а применением , определенная в Prelude таким образом, что ( эквивалентно недопустимому ранее сечению. Для той же цели может служить выражение (+ (- exp)).
Трансляция:
Выполняются следующие
(
(e
где - e - выражение, а x - переменная, которая не является свободной в e.
exp |
-> |
if exp1 then exp2 else exp3 |
Перевод:
| выражение | -> |
if выражение1 then выражение2 else выражение3 |
Условное выражение имеет вид if e1 then e2 else e3 и возвращает: значение e2 - если значение e1 равно True, e3 - если e1 равно False, и _|_ - иначе.
Трансляция:
Выполняются следующие
if e1 then e2 else e3= case e1 of { True -> e2 ; False -> e3 }
где True и False - конструкторы с нулевым числом аргументов из типа Bool, определенные в Prelude. Тип e1 должен быть Bool, e2 и e3 должны иметь тот же тип, который также является типом всего условного выражения.
exp |
-> |
exp1 qop exp2 |
|
aexp |
-> |
[ exp1 , ... , expk ] |
(k>=1) |
| | | gcon |
||
gcon |
-> |
[] | |
| | | qcon |
( gconsym ) |
|
qcon |
-> |
||
qop |
-> |
qconop |
|
qconop |
-> |
gconsym |
|
gconsym |
-> |
: |
Перевод:
| выражение | -> |
выражение1 квалифицированный-оператор выражение2 | |
| выражение-аргумента | -> |
[ выражение1 , ... , выражениеk ] | (k >= 1) |
| | | общий-конструктор | ||
| общий-конструктор | -> |
[] | |
| | | квалифицированный-конструктор | ||
| квалифицированный-конструктор | -> |
( символ-общего-конструктора ) | |
| квалифицированный-оператор | -> |
квалифицированный-оператор-конструктора | |
| квалифицированный-оператор-конструктора | -> |
символ-общего-конструктора | |
| символ-общего-конструктора | -> |
: |
Списки записываются в виде [e1, ..., ek], где k >= 1. Конструктором списка является :, пустой список обозначается []. Стандартные операции над списками описаны в Prelude (см. раздел 6.1.3 и лекции 8, особенно раздел 8.1).
Трансляция:
Выполняются следующие
[e1, ..., ek]= e1 : (e2 : ( ... (ek : [])))
где : и [] - конструкторы списков, определенные в Prelude (см. раздел 6.1.3). Выражения e1, .... , ek должны быть одного типа (назовем его t ), а типом всего выражения является [t] (см. раздел 4.1.2).
Конструктор : предназначен исключительно для построения списка; как и [], он является обоснованной частью синтаксиса языка и не может быть скрыт или переопределен. Он представляет собой правоассоциативный оператор с уровнем приоритета 5 (раздел 4.4.2).
aexp |
-> |
( exp1 , ... , expk ) |
(k>=2) |
| | | qcon |
||
qcon |
-> |
(,{,}) |
Перевод:
| выражение-аргумента | -> |
( выражение1 , ... , выражениеk ) | (k>=2) |
| | | квалифицированный-конструктор | ||
| квалифицированный-конструктор | -> |
(,{,}) |
Кортежи записываются в виде (e1, ..., ek) и могут быть произвольной длины k >= 2. Конструктор для кортежа размера n обозначается (,...,), где n-1 запятых. Таким образом, (a,b,c) и (,,) a b c обозначают одно и то же значение. Стандартные операции над кортежами описаны в Prelude (см. раздел 6.1.4 и лекцию 8).
Трансляция:
(e1, ..., ek) для k >= 2 является экземпляром кортежа размера k, в соответствии с определением в Prelude, и не требует трансляции. Если t1, ... , tk - соответственно типы e1, ... , ek, то типом кортежа будет (t1, ..., tk) (см. раздел 4.1.2).
aexp |
-> |
gcon |
| | | ( exp ) |
|
gcon |
-> |
() |
Перевод:
| выражение-аргумента | -> |
общий-конструктор |
| | | ( выражение ) | |
| общий-конструктор | -> |
() |
Выражение вида (e) представляет собой просто выражение в скобках и эквивалентно e. Единичное выражение () имеет тип () (см. раздел 4.1.2). Этот единственный член указанного типа, отличный от _|_, может рассматриваться как "кортеж нулевого размера" (см. раздел 6.1.5).
Трансляция:
(e) эквивалентно e
aexp |
-> |
[ exp1 [, exp2] .. [exp3] ] |
Перевод:
| выражение-аргумента | -> |
[ выражение1 [, выражение2] .. [выражение3] ] |
Арифметическая последовательность [e1, e2 .. e3] обозначает список значений типа t, где каждое из выражений ei имеет тип t, и t является экземпляром класса Enum.
Трансляция:
Для арифметических последовательностей выполняются следующие
[ e1 ... ] = enumFrom e1
[ e1,e2 ... ] = enumFromThen e1 e2
[ e1 ... e3 ] = enumFromTo e1 e3
[ e1,e2 ... e3 ]= enumFromThenTo e1 e2 e3
где enumFrom, enumFromThen, enumFromTo и enumFromThenTo являются методами класса Enum, определенные в Prelude (см. раздел 6.3.2).
Семантика арифметических последовательностей поэтому полностью зависит от объявления экземпляра для типа t. Для получения более детальной информации о том, какие типы Prelude являются подтипами Enum и какова их семантика, смотрите рздел 6.3.4.
aexp |
-> |
[ exp | qual1 , ... , qualn ] |
(описание списка, n >= 1) |
qual |
-> |
|
(генератор) |
| | | let decls |
(локальное объявление) | |
| | | exp |
(страж) |
Перевод:
| выражение-аргумента | -> |
[ выражение | квалификатор1 , ... , квалификаторn ] | (описание списка, n>=1 ) |
| квалификатор | -> |
образец <- выражение | (генератор) |
| | | let списки-объявлений | (локальное объявление) | |
| | | выражение | (страж) |
Описание списка имеет вид [ e | q1, ..., qn ], n>=1, где квалификаторы qi являются
p 7 >= e, где p - образец (см. раздел 3.17) типа t, а e - выражение типа [t],Такое описание списка возвращает список элементов, порожденный путем вычисления e в последовательных окружениях, созданных вложенным вычислением вглубину генераторов в списке квалификаторов. Связывание имен переменных происходит согласно правилам обычного сопоставления с образцом (см. раздел 3.17), и если сопоставление завершится неудачей, то соответствующий элемент списка будет просто пропущен. Таким образом,
[ x | xs <- [ [(1,2),(3,4)], [(5,4),(3,2)] ],
(3,x) <- xs ]
порождает список [4,2]. Если квалификатор является стражем, то, для того чтобы предшествующее сопоставление с образцом завершилось успешно, необходимо, чтобы значение квалификатора равнялось True. Как обычно, связывания имен в описаниях списков могут скрыть связывания имен во внешних областях видимости, например,
[ x | x <- x, x <- x ]= [ z | y <- x, z <- y]
Трансляция:
Для описаний списков выполняются следующие
[ e | True ] = [e]
[ e | q ] = [ e | q, True ]
[ e | b, Q ] = if b then [ e | Q ] else []
[ e | p <- l, Q ] = let ok p = [ e | Q ]
ok _ = []
in concatMap ok l
[ e | let decls, Q ]= let decls in [ e | Q ]
где e - выражения, p - образцы, l - выражения, значениями которых являются списки, b - булевы выражения, decls - списки объявлений, q - квалификаторы, а Q - последовательности квалификаторов. ok - новая переменная. Функция concatMap и булево значение True определены в Prelude.
Как показывает трансляция описаний списков, переменные, связанные с помощью let, имеют полностью полиморфные типы, тогда как переменные, определенные с помощью >-, связаны лямбда-выражением и поэтому мономорфны (см. раздел 4.5.4).
exp |
-> |
let decls in exp |
Перевод:
| выражение | -> |
let списки-объявлений in выражение |
Let-выражения имеют общий вид let { d1 ; ... ; dn } in e и вводят вложенный, лексически ограниченный, взаимно рекурсивный список объявлений (в других языках let часто называют letrec ). Областью видимости объявлений является выражение e и правая часть объявлений. Объявления описаны в лекции 4. Сопоставление и связывание образцов выполняется лениво, неявная - делает эти образцы неопровержимыми. Например,
let (x,y) = undefined in e
не вызовет ошибку времени выполнения до тех пор, пока x или y не будут вычислены.
Трансляция:
Динамическая семантика выражения let { d1 ; ... ; dn } в e0 охватывается следующей трансляцией: после удаления всех сигнатур типов каждое объявление di транслируется в уравнение вида pi = ei, где pi и ei - соответственно образцы и выражения, при этом используется трансляция в разделе 4.4.3. Однажды сделав это, эти
let {p1=e1; ... ; pn=e1} in e0 = let (~p1, ... ,~pn) = (e1, ... ,en) in e0
let p = e1 in e0 = case e1 of ~p -> e0
где ни одна переменная в p не является свободной в e1
let p = e1 in e0 = let p = fix ( \ ~p -> e1) in e0
где fix - наименьший ассоциативный оператор. Обратите внимание на использование неопровержимого образца ~ p. Эта трансляция не сохраняет статическую семантику, потому что использование case препятствует полностью
exp |
-> |
case exp of { alts } |
|
alts |
-> |
alt1 ; ... ; altn |
(n>=1) |
alt |
-> |
|
|
| | | |
||
| | | (пустая альтернатива) | ||
gdpat |
-> |
gd -> exp [ gdpat ] |
|
gd |
-> |
| exp0 |
Перевод:
| выражение | -> |
case выражение of { список-альтернатив | |
| список-альтернатив | -> |
альтернатива1 ; ... ; альтернативаn | (n>=1) |
| альтернатива | -> |
образец -> выражение [where список-объявлений] |
|
| | | образец образец-со-стражами [where список-объявлений] | ||
| | | (пустая альтернатива) | ||
| образец-со-стражами | -> |
страж -> выражение [ образец-со-стражами ] |
|
| страж | -> |
| выражение0 |
case e of { p1 match1 ; ... ; pn matchn }
где каждый matchi имеет общий вид
| gi1-> ei1 ... | gimi-> eimi where declsi
(Заметьте, что в синтаксическом правиле для gd "|" является pi matchi состоит из образца pi и его сопоставлений matchi. Каждое сопоставление, в свою очередь, состоит из последовательности пар стражей gij и тел eij (выражений), за которыми следуют необязательные связывания (declsi), чья область видимости распространяется над всеми стражами и выражениями альтернативы. Альтернатива вида
pat -> exp where decls
интерпретируется как краткая запись для
pat | True-> exp where decls
Вычисление e отдельным альтернативам. Альтернативы проверяются последовательно, сверху вниз. Если e соответствует образцу в альтернативе, выполняется связывание переменных, сначала указанных в образце, а затем - с помощью declsi в операторе where, связанном с этой альтернативой. Если значение одного из вычисляемых стражей окажется True, в том же окружении, что и страж, будет вычислена соответствующая правая часть. Если значения всех стражей окажутся False, процесс сопоставления с образцом будет возобновлен со следующей альтернативы. Если не удастся сопоставить ни один образец, результатом будет _|_. Сопоставление с образцом описано в разделе 3.17, а формальная семантика
Замечание о разборе. Выражение
case x of { (a,_) | let b = not a in b :: Bool -> a }
нелегко правильно интерпретировать при разборе. Оно имеет единственную однозначную интерпретацию, а именно:
case x of { (a,_) | (let b = not a in b :: Bool) -> a }
Тем не менее, выражение Bool -> a является синтаксически правильным типом, и синтаксические анализаторы с ограниченным предварительным просмотром могут выбрать этот неправильный вариант, и тогда программа будет признана недопустимой. Поэтому мы советуем программистам избегать использования стражей, которые заканчиваются указанием сигнатуры типа, именно поэтому gd содержит exp0, а не не exp.
exp |
-> |
do { stmts } |
(do-выражение) |
stmts |
-> |
stmt1 ... stmtn exp [;] |
(n>=0) |
stmt |
-> |
exp ; |
|
| | | |
||
| | | let decls ; |
||
| | | ; | (пустая инструкция) |
Перевод:
| выражение | -> |
do { список-инструкций } | (do-выражение) |
| список-инструкций | -> |
инструкция1 ... инструкцияn выражение [;] | (n>=0) |
| инструкция | -> |
выражение ; | |
| | | образец \gets выражение ; | ||
| | | let список-объявлений ; | ||
| | | ; | (пустая инструкция) |
Do-выражения предоставляют более удобный синтаксис для монадического программирования. Оно позволяет записать такое выражение
putStr "x: " >> getLine >>= \l -> return (words l)
в более традиционном виде:
do putStr "x: "
l <- getLine
return (words l)
Трансляция:
Для do-выражений выполняются следующие
do {e} = e
do {e;stmts} = e >> do {stmts}
do {p <- e; stmts} = let ok p = do {stmts}
ok _ =
in e >>= ok
do {let decls; stmts} = let decls in do {stmts}
Пропуски "..." обозначают генерируемое компилятором сообщение об ошибке, передаваемое функции , желательно давая некоторое указание на местоположение ошибки сопоставления с образцом; функции >>, >>= и являются операциями в классе ok является новым идентификатором.
Как показано в трансляции do, переменные, связанные let, имеют полностью полиморфные типы, тогда как те переменные, которые определены с помощью <-, являются связанными лямбда-выражением и поэтому являются мономорфными.
Объявление типа данных может содержать необязательные определения имен полей (см. раздел 4.2.1). Эти имена полей можно использовать для создания, извлечения и обновления полей способом, который не зависит от всей структуры типа данных.
Различные типы данных не могут совместно использовать общие имена полей в одной области видимости. Имя поля можно использовать не более одного раза в конструкторе. В пределах типа данных, тем не менее, имя поля можно использовать в более чем одном конструкторе, при условии, что поле имеет один и тот же тип во всех конструкторах. Для того чтобы проиллюстрировать последнее замечание, рассмотрим:
data S = S1 { x :: Int } | S2 { x :: Int } - OK
data T = T1 { y :: Int } | T2 { y :: Bool } - ПЛОХО
Здесь S является допустимым типом данных, а T - нет, потому что в последнем случае для y указан другой тип, противоречащий указанному ранее.
aexp |
-> |
qvar |
Перевод:
| выражение-аргумента | -> |
квалифицированная-переменная |
Имена полей используются в качестве селекторной функции. Когда имя поля используется в качестве переменной, оно действует как функция, которая извлекает поле из объекта. Селекторы являются связываниями верхнего уровня, и поэтому они могут быть перекрыты локальными переменными, но не могут конфликтовать с другими связываниями верхнего уровня с тем же именем. Это сокрытие затрагивает только селекторные функции, при создании записей (раздел 3.15.2) и их обновлении (раздел 3.15.3) имена полей не могут быть спутаны с обычными переменными.
Трансляция:
Имя поля f представляет собой селекторную функцию в соответствии с определением:
f x = case x of { C1 p11... p1k -> e1; ... ; Cn pn1 ... pnk -> en }
где все C1 ...Cn - f, pij - это y, когда f именует собой j -ю компоненту Ci, или _ иначе, а ei - это y, когда некоторое поле в Ci имеет имя f, или undefined иначе..
aexp |
-> |
qcon { fbind1 , ... , fbindn } |
(именованная конструкция, n>=0 ) |
fbind |
-> |
qvar = exp |
Перевод:
| выражение-аргумента | -> |
квалифицированный-конструктор { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованная конструкция, n>=0 ) |
| связывание-имени-поля | -> |
квалифицированная-переменная = выражение |
Конструктор с именованными полями может использоваться для создания значения, в котором компоненты задаются именем, а не позицией. В отличие от фигурных скобок, используемых в списках объявлений, здесь присутствие фигурных скобок не зависит от размещения текста, символы { и } должны использоваться явно. (То же самое относится к обновлению полей и образцам полей.) Создание объектов с использованием имен полей подчинено следующим ограничениям:
_|_.Выражение F {}, где F - конструктор данных, является допустимым независимо от того, было или нет F объявлено с использованием синтаксиса записи (при условии, что F не имеет обязательных полей, см. пункт третий в приведенном выше списке); оно обозначает F _|_1 ... _|_n, где n - число аргументов F.
Трансляция:
В связывании f = v поле f именует v.
C { bs } = C (
где k - число аргументов C.
Вспомогательная функция определена следующим образом:
Если i-ый компонент конструктора C имеет имя поля f и если f=v появляется в списке связываний bs, то равно v. Иначе равно значению по умолчанию d.
aexp |
-> |
aexp<qcon> { fbind1 , ... , fbindn } |
(именованное обновление, n>=1 ) |
Перевод:
| выражение-аргумента | -> |
выражение-аргумента<квалифицированный-конструктор> { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованное обновление, n>=1 ) |
Значения, принадлежащие типу данных с именованными полями, можно обновлять, не боясь разрушить структуру данных. При этом создается новое значение, в котором заданные значения полей замещают те, что были в существующем значении. Обновления подчиняются следующим правилам:
Трансляция:
Используя предыдущее определение функции ,
e { bs }= case e of
C1 v1 ... vk1 -> C1 (
…
Cj v1 ... vkj -> Cj (
-> error "Ошибка обновления"
где {C1,...,Cj} - набор конструкторов, содержащих все имена в bs, а ki - число аргументов Ci.
Вот некоторые примеры, использующие именованные поля:
data T = C1 {f1,f2 :: Int}
| C2 {f1 :: Int,
f3,f4 :: Char}
| Выражение | Трансляция |
|---|---|
C1 {f1 = 3} |
C1 3 undefined |
C2 {f1 = 1, f4 = 'A', f3 = 'B'} |
C2 1 'B' 'A' |
x {f1 = 1} |
case x of C1 _ f2 -> C1 1 f2 |
C2 _ f3 f4 -> C2 1 f3 f4 |
Поле f1 является общим для обоих конструкторов в T. Этот пример транслирует выражения, использующие конструкторы, записываемые с именами полей, в x {f2 = 1, f3 = 'x'}, произойдет ошибка компиляции.
exp |
-> |
exp :: [context =>] type |
Перевод:
| выражение | -> |
выражение :: [контекст => ] тип |
Сигнатуры типов выражений имеют вид e :: t, где e - выражение, а t - тип (раздел 4.1.2); они используются для явного указания типа выражения, в частности, для того чтобы разрешить неоднозначность типов из-за перегрузки (см. раздел 4.3.4). Значением выражения является значение exp. Как и с обычными сигнатурами типов (см. раздел 4.4.1), объявленный тип может быть более частным, чем основной тип, выводимый из exp, но будет ошибкой указать тип, который окажется более общим или не сопоставимым с основным типом.
Трансляция:
e :: t = let { v :: t; v = e } in v
Образцы появляются в лямбда-абстракциях, определениях функций, связываниях с образцом, описаниях списков, do-выражениях и case-выражениях. Тем не менее, первые пять из них в конечном счете транслируются в
Образцы имеют следующий синтаксис:
|
-> |
var + integer |
(образец упорядочивания) |
| | | |
||
|
-> |
|
|
| | | lpati |
||
| | | rpati |
||
lpati |
-> |
(lpati | |
|
lpat6 |
-> |
- (integer | float) |
(отрицательный литерал) |
rpati |
-> |
|
|
|
-> |
apat |
|
| | | gcon apat1 ... apatk |
(число аргументов конструктора gcon = k, k >= 1 ) |
|
apat |
-> |
var [@ apat] |
("такой как"-образец) |
| | | gcon |
(число аргументов конструктора gcon = 0 ) |
|
| | | qcon { fpat1 , ... , fpatk } |
(именованный образец, k >= 0 ) |
|
| | | |
||
| | | _ | (любые символы) | |
| | | ( |
(образец в скобках) | |
| | | ( |
(образец кортежа, k >= 2 ) |
|
| | | [ |
(образец списка, k >= 1 ) |
|
| | | ~apat |
(неопровержимый образец) | |
fpat |
-> |
qvar = |
Перевод:
| образец | -> |
переменная + целый-литерал | (образец упорядочивания) |
| | | образец0 | ||
| образецi | -> |
образецi+1 [квалифицированный-оператор-конструктора(n,i) образецi+1] | |
| | | левый-образецi | ||
| | | правый-образецi | ||
| левый-образецi | -> |
(левый-образецi | образецi+1) квалифицированный-оператор-конструктора(l,i) образецi+1 | |
| левый-образец6 | -> |
- (целый-литерал | литерал-с-плавающей-точкой) | (отрицательный литерал) |
| правый-образецi | -> |
образецi+1 квалифицированный-оператор-конструктора(r,i) (правый-образецi | образецi+1) | |
| образец10 | -> |
такой-как-образец | |
| | | общий-конструктор такой-как-образец1 ... такой-как-образецk | (число аргументов конструктора gcon = k, k >= 1 ) |
|
| такой-как-образец | -> |
переменная [@ такой-как-образец] | ("такой как"-образец) |
| | | общий-конструктор | (число аргументов конструктора gcon = 0) | |
| | | квалифицированный-конструктор { образец-с-именем1 , ... , образец-с-именемk } | (именованный образец, k >= 0 ) |
|
| | | литерал | ||
| | | _ | (любые символы) | |
| | | ( образец ) | (образец в скобках) | |
| | | ( образец1 , ... , образецk ) | (образец кортежа, k >= 2 ) |
|
| | | [ образец1 , ... , образецk ] | (образец списка, k>=1 ) |
|
| | | ~ такой-как-образец |
(неопровержимый образец) | |
| образец- | -> |
квалифицированная-переменная = образец |
Число аргументов конструктора должно соответствовать числу образцов, связанных с ним; нельзя сопоставлять частично примененный конструктор.
Все образцы должны быть линейными : ни одна переменная не может появляться более одного раза. Например, следующее определение недопустимо:
f (x,x) = x - ЗАПРЕЩЕНО; x дважды используется в образце
Образцы вида var@ называются "такими как" - образцами, и позволяют использовать var в качестве имени для значения, сопоставляемого . Например,
case e of { xs@(x:rest) -> if x==0 then rest else xs }
эквивалентено
let { xs = e } in
case xs of { (x:rest) -> if x==0 then rest else xs }
Образцы вида _ обозначают группы любых символов и полезны, когда некоторая часть образца не используется в правой части. Это как если бы идентификатор, не используемый где-либо в другом месте, был помещен на свое место. Например,
case e of { [x,_,_] -> if x==0 then True else False }
эквивалентно
case e of { [x,y,z] -> if x==0 then True else False }
Образцы сопоставляются значениям. Попытка сопоставить образец может иметь один из трех результатов: потерпеть неудачу, иметь успех, при этом каждая переменная в образце связывается с соответствующим значением, или быть отклонена (т.е. вернуть _|_). Сопоставление с образцом выполняется слева направо и извне вовнутрь, в соответствии со следующими правилами:
var значению v всегда имеет успех и связывает var с v.~apat значению v всегда имеет успех. apat связываются с соответствующими значениями, если сопоставление apat с v завершится успешно, или с _|_, если сопоставление apat с v потерпит неудачу или будет отклонено. (Связывание не подразумевает вычисление.)С точки зрения операций, это означает, что никакое сопоставление не будет сделано с образцом ~apat до тех пор, пока одна из переменных в apat не будет использована. В этот момент весь образец сопоставляется значению, и если сопоставление потерпит неудачу или будет отклонено, так выполняется все вычисление.
con pat значению, где con - конструктор, определенный с помощью newtype, зависит от значения:con v, то pat сопоставляется v._|_, то pat сопоставляется _|_.То есть конструкторы, связанные с newtype, служат только для того, чтобы изменить тип значения.
con pat 1 ...pat n значению, где con - конструктор, определенный с помощью data, зависит от значения:con v1 ...vn, части образца сопоставляются слева направо компонентам значения данных, если все сопоставления завершатся успешно, результатом всего сопоставления будет успех; первая же неудача или отклонение приведут к тому, что сопоставление с образцом соответственно потерпит неудачу или будет отклонено.con' v1 ...vm, где con - конструктор, отличный от con', сопоставление потерпит неудачу._|_, сопоставление будет отклонено.k
значениюv имеет успех, если v == k, где == перегружен на основании типа образца. Сопоставление будет отклонено, если эта проверка будет отклонена.Интерпретация числовых литералов в точности описана в разделе 3.2, то есть fromInteger или fromRational применяется к литералу типа Integer или Rational (соответственно) для преобразования его к соответствующему типу.
n+k -образца (где n - переменная, а k - положительный целый литерал) значению v имеет успех, если x >= k, при этом n связывается с x - k, и терпит неудачу иначе. Снова, функции >= и - являются перегруженными в зависимости от типа образца. Сопоставление будет отклонено, если сравнение будет отклонено.Интерпретация литерала k является точно такой же, как и для числовых литералов, за исключением того, что допустимы только целые литералы.
var@apat значению v является результатом сопоставления apat с v, дополненного связыванием var с v. Если сопоставление apat с v потерпит неудачу или будет отклонено, такой же результат будет у сопоставления с образцом.Помимо очевидных ограничений
Num.Fractional .n+k -образец можно сопоставить только значению в классе Integral.Многие люди считают, что n+k -образцы не следует использовать. Эти образцы могут быть удалены или изменены в будущих версиях
Иногда полезно различать два вида образцов. Сопоставление с неопровержимым образцом не является строгим: образец сопоставляется, даже если сопоставляемое значение равно _|_ _. Сопоставление с опровержимым образцом является строгим: если сопоставляемое значение равно _|_сопоставление будет отклонено. Неопровержимыми являются следующие образцы: переменная, символ подчеркивания, N apat, где N - конструктор, определенный с помощью newtype, а apat - неопровержимый образец (см. раздел 4.2.3), var@apat, где apat - неопровержимый образец, и образцы вида ~apat (независимо от того, является ли apat неопровержимым образцом или нет). Все остальные образцы являются опровержимыми.
Приведем несколько примеров:
['a','b'] сопоставляется ['x' _|_, ], то сопоставление 'a' с 'x' потерпит неудачу, и все сопоставление завершится неудачей. Но если ['a','b'] сопоставляется [_|_ ,'x'], то попытка сопоставить 'a' с _|_ приведет к тому, что все сопоставление будет отклонено.(\ ~(x,y) -> 0) _|_ => 0 (\ (x,y) -> 0) _|_ => _|_ (\ ~[x] -> 0) [] => 0 (\ ~[x] -> x) [] => _|_ (\ ~[x,~(a,b)] -> x) [(0,1),_|_] => (0,1) (\ ~[x, (a,b)] -> x) [(0,1),_|_] => _|_ (\ (x:xs) -> x:x:xs) _|_ => _|_ (\ ~(x:xs) -> x:x:xs) _|_ => _|_:_|_:_|_
newtype N = N Bool data D = D !Bool
Эти примеры показывают различие между типами, определенными с помощью data и newtype, при сопоставлении с образцом:
(\ (N True) -> True) _|_ => _|_ (\ (D True) -> True) _|_ => _|_ (\ ~(D True) -> True) _|_ => True
Дополнительные примеры вы найдете в разделе 4.2.3.
Образцы верхнего уровня в case-выражениях и набор образцов верхнего уровня в связываниях имен функций или образцах могут иметь ноль или более связанных с ними стражей. Страж - это булево выражение, которое вычисляется только после того, как все аргументы были успешно сопоставлены, и, для того чтобы все сопоставление с образцом имело успех, значением этого выражения должно быть истина. Окружением стража является то же окружение, что и для правой части альтернативы
Семантика стража имеет очевидное влияние на характеристики строгости функции или
f :: (Int,Int,Int) -> [Int] -> Int f ~(x,y,z) [a] | (a == y) = 1
и a, и y будут вычислены с помощью == в страже.
Семантика всех конструкций сопоставления с образцом, отличных от case -выражений, определена с помощью case -выражениями. В свою очередь, семантика самих
(a) case e of { alts } = (\v -> case v of { alts }) e
где v - новая переменная
(b) case v of { p1 match1; ... ; pn matchn }
= case v of { p1 match1 ;
_ -> ... case v of {
pn matchn ;
_ -> error "Нет сопоставлений" }...}
где каждое matchi имеет вид:
| gi,1 -> ei,1 ; ... ; | gi,mi -> ei,mi where { declsi }
(c) case v of { p | g1 -> e1 ; ...
| gn -> en where { decls }
_ -> e' }
= case e' of
{y -> (где y - новая переменная)
case v of {
p -> let { decls } in
if g1 then e1 ... else if gn then en else y ;
_ -> y }}
(d) case v of { ~p -> e; _ -> e' }
= (\x1 ... xn -> e ) (case v of { p-> x1 }) ... (case v of { p -> xn})
где x1, ..., xn - переменные в p
(e) case v of { x@p -> e; _ -> e' }
= case v of { p -> ( \ x -> e ) v ; _ -> e' }
(f) case v of { _ -> e; _ -> e' } = e
(g) case v of { K p1 ...pn -> e; _ -> e' }
= case v of {
K x1 ...xn -> case x1 of {
p1 -> ... case xn of { pn -> e ; _ -> e' } ...
_ -> e' }
_ -> e' }
по меньшей мере один из p1, ..., pn не является переменной; x1, ..., xn - новые переменные
(h) case v of { k -> e; _ -> e' } = if (v==k) then e else e'
где k - числовой, символьный или строковый литерал
(i) case v of { x -> e; _ -> e' } = case v of { x -> e }
(j) case v of { x -> e } = ( \ x -> e ) v
(k) case N v of { N p -> e; _ -> e' }
= case v of { p -> e; _ -> e' }
где N - конструктор newtype
(l) case _|_ of { N p -> e; _ -> e' } = case _|_ of { p -> e }
где N - конструктор newtype
(m) case v of { K { f1 = p1 , f2 = p2 , ... } -> e ; _ -> e' }
= case e' of {
y ->
case v of {
K { f1 = p1 } ->
case v of { K { f2 = p2 , ... } -> e ; _ -> y };
_ -> y }}
где f1, f2, ... - поля конструктора K, y- новая переменная
(n) case v of { K { f = p } -> e ; _ -> e' }
= case v of {
K p1 ... pn -> e ; _ -> e' }
где pi равно p, если f именует i-ую компоненту K, _ иначе
(o) case v of { K {} -> e ; _ -> e' }
= case v of {
K _ ... _ -> e ; _ -> e' }
(p) case (K' e1 ... em) of { K x1 ... xn -> e; _ -> e' } = e'
где K и K' - различные конструкторы data соответственно с n и m аргументами
(q) case (K e1 ... en) of { K x1 ... xn -> e; _ -> e' }
= (\x1 ... xn -> e) e1 ... en
где K - конструктор data с n аргументами
(r) case _|_ of { K x1 ... xn -> e; _ -> e' } = _|_
где K - конструктор data с n аргументами
(s) case v of { x+k -> e; _ -> e' }
= if v >= k then (\x -> e) (v-k) else e'
где k - числовой литерал
На рис. 3.1-3.2: e, e' и ei - выражения, g и gi - булевы выражения, p и pi - образцы, v, x и xi - переменные, K и K' - конструкторы алгебраических типов данных (data) (включая конструкторы кортежей), а N - конструктор newtype.
Правило (b) соответствует основному case -выражению исходного языка, независимо от того, включает ли оно стражей: если стражи не указаны, то в формах matchi вместо стражей gi,j будет подставлено True. Последующие
Правило (h) на 3.2 затрагивает == ; именно это правило определяет смысл сопоставления образца с перегруженными константами.
Все эти (d), (e), (j), (q) и (s) используют лямбду, а не let ; это указывает на то, что переменные, связанные с помощью case, являются мономорфными (раздел 4.1.4).
За исключением let -выражений эти трансляции сохраняют и статическую, и динамическую семантику. Prelude. Например, "concatMap", используемая в трансляции описания списка (раздел 3.11), обозначает concatMap, определенную в Prelude, невзирая на то, находится ли идентификатор "concatMap" в области видимости, где используется описание списка, и (если находится в области видимости) к чему он привязан.
В синтаксисе, который следует далее, есть некоторые семейства нетерминалов, индексированные уровнями приоритета (записанными как верхний индекс). Аналогично, нетерминалы (оператор), varop (оператор-переменной) и conop (оператор-конструктора) могут иметь двойной индекс: букву l, r или n соответственно для левоассоциативности, правоассоциативности или отсутствия ассоциативности и уровень приоритета. Переменная уровня приоритета i изменяется в пределах от 0 до 9, переменная ассоциативности a изменяется в диапазоне {l, r, n}. Например,
aexp |
-> |
( expi+1 qop(a,i) ) |
на самом деле обозначает 30 правил вывода с 10 подстановками для i и 3 для a.
| exp | -> |
exp0 :: [context =>] type |
(сигнатура типа выражения) |
| | | exp0 |
||
expi |
-> |
expi+1 [qop(n,i) expi+1] |
|
| | | lexpi |
||
| | | rexpi |
||
lexpi |
-> |
(lexpi | expi+1) qop(l,i) expi+1 |
|
lexp6 |
-> |
- exp7 |
|
rexpi |
-> |
expi+1 qop(r,i) (rexpi | expi+1) |
|
exp10 |
-> |
\ apat_1 ... apat_n -> exp |
(лямбда-абстракция, n>= 1 ) |
| | | let decls in exp |
(let-выражение) | |
| | | if exp then exp else exp |
(условное выражение) | |
| | | case exp of { alts } |
( |
|
| | | do { stmts } |
(do-выражение) | |
| | | fexp |
||
fexp |
-> |
[fexp] aexp |
(применение функции) |
aexp |
-> |
qvar |
(переменная) |
| | | gcon |
(общий конструктор) | |
| | | |
||
| | | ( exp ) |
(выражение в скобках) | |
| | | ( exp1 , ... , expk ) |
(кортеж, k>= 2 ) |
|
| | | [ exp1 , ... , expk ] |
(список, k>= 1 ) |
|
| | | [ exp1 [, exp2] .. [exp3] ] |
(арифметическая последовательность) | |
| | | [ exp | qual1 , ... , qualn ] |
(описание списка, n>= 1 ) |
|
| | | ( expi+1 qop(a,i) ) |
(левое сечение) | |
| | | ( lexpi qop(l,i) ) |
(левое сечение) | |
| | | ( qop(a,i)<-> expi+1 ) |
(правое сечение) | |
| | | ( qop(r,i)<-> rexpi ) |
(правое сечение) | |
| | | qcon { fbind1 , ... , fbindn } |
(именованная конструкция, n>= 0) |
|
| | | aexp<qcon> { fbind1 , ... , fbindn } |
(именованное обновление, n >= 1) |
Перевод:
| выражение | -> -> |
выражение^0 :: [контекст => ] тип |
(сигнатура типа выражения) |
| | | выражение0 | ||
| выражениеi | -> |
выражениеi+1 [квалифицированный-оператор(n,i) выражениеi+1] | |
| | | левое-сечение-выраженияi | ||
| | | правое-сечение-выраженияi | ||
| левое-сечение-выраженияi | -> |
(левое-сечение-выраженияi | выражениеi+1) квалифицированный-оператор(l,i) выражениеi+1 | |
| левое-сечение-выражения6 | -> |
- выражение7 | |
| правое-сечение-выраженияi | -> |
выражениеi+1 квалифицированный-оператор(r,i) (правое-сечение-выраженияi | выражениеi+1) | |
| выражение10 | -> |
\ такой-как-образец1 ... такой-как-образецn -> выражение |
(лямбда-абстракция, n>= 1) |
| | | let списки-объявлений in выражение |
( let -выражение) |
|
| | | if выражение then выражение else выражение |
(условное выражение) | |
| | | case выражение of { список-альтернатив } |
( case -выражение) |
|
| | | do { список-инструкций } |
( do -выражение) |
|
| | | функциональное-выражение | ||
| функциональное-выражение | -> |
[функциональное-выражение] выражение-аргумента | (применение функции) |
| выражение-аргумента | -> |
квалифицированная-переменная | (переменная) |
| | | общий-конструктор | (общий конструктор) | |
| | | литерал | (выражение в скобках) | |
| | | ( выражение ) | ||
| | | ( выражение1 , ... , выражениеk ) | (кортеж, k>= 2) |
|
| | | [ выражение1 , ... , выражениеk ] | (список, k >= 1) |
|
| | | [ выражение1 [, выражение2] .. [выражение3] ] | (арифметическая последовательность) | |
| | | [ выражение | квалификатор1 , ... , квалификаторn ] | (описание списка, n >= 1) |
|
| | | ( выражениеi+1 квалифицированный-оператор(a,i) ) | (левое сечение) | |
| | | ( левое-сечение-выраженияi квалифицированный-оператор(l,i) ) | (левое сечение) | |
| | | ( квалифицированный-оператор(a,i)<-> выражениеi+1 ) | (правое сечение) | |
| | | ( квалифицированный-оператор(r,i)<-> правое-сечение-выраженияi ) | (правое сечение) | |
| | | квалифицированный-конструктор { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованная конструкция, n>=0) |
|
| | | выражение-аргумента<квалифицированный-конструктор> { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованное обновление, n>=1) |
Неоднозначность выражений, включая инфиксные операторы, разрешается с помощью ассоциативности и приоритета оператора (см. раздел 4.4.2). Следующие друг за другом операторы без скобок, имеющие один и тот же приоритет, должны быть оба либо левоассоциативными, либо правоассоциативными, во избежание синтаксической ошибки. Для заданного выражения без скобок "x qop(a,i) y qop(b,j) z", где qop - оператор, часть "x qop(a,i) y" или "y qop(b,j) z" следует взять в скобки, когда i=j, за исключением a=b=l или a=b=r.
Отрицание является единственным -, определенный в Prelude (см. раздел 4.4.2).
Грамматика является неоднозначной по отношению к пространству лямбда-абстракций, let-выражений и условных выражений. Неоднозначность разрешается с помощью мета-правила, согласно которому каждая из этих конструкций рассматривается слева направо насколько это возможно.
Примеры интерпретации при разборе показаны ниже.
| Это | интерпретируется как |
|---|---|
f x + g y |
(f x) + (g y) |
- f x + y |
(- (f x)) + y |
let { ... } in x + y |
let { ... } in (x + y) |
z + let { ... } in x + y |
z + (let { ... } in (x + y)) |
f x y :: Int |
(f x y) :: Int |
\ x -> a+b :: Int |
\ x -> ((a+b) :: Int) |
Замечание относительно разбора. Выражения, которые затрагивают взаимодействие ассоциативности и приоритетов с применением мета-правила для let/лямбда, могут оказаться трудными для разбора. Например, выражение
let x = True in x == x == True
не может означать
let x = True in (x == x == True)
потому что (==) является неассоциативным оператором, поэтому выражение должно быть интерпретировано таким образом:
(let x = True in (x == x)) == True
Тем не менее, реализации могут прекрасно обойтись дополнительным проходом после завершения разбора, чтобы обработать ассоциативность и приоритеты операторов, поэтому они могут спокойно передать предыдущий неправильный разбор. Мы советуем программистам избегать использования конструкций, чей разбор затрагивает взаимодействие (отсутствия) ассоциативности с применением мета-правила для let/лямбда.
Ради ясности остальная часть этого раздела описывает синтаксис выражений без указания их приоритетов.
Ошибки во время вычисления выражений, обозначаемые как _|_, в программе на _|_. Другими словами, значение любого типа может быть связано с вычислением, которое будет завершено, когда потребуется результат вычисления, и завершится с ошибкой. При своем вычислении ошибки вызывают немедленное завершение программы и не могут быть отловлены пользователем. Prelude содержит две функции, которые сразу вызывают такие ошибки:
error :: String -> a undefined :: a
Вызов функции error завершает выполнение программы и возвращает в операционную систему соответствующий признак ошибки. Он также должен вывести на экран строку некоторым, зависящим от системы, способом. Когда используется функция undefined, сообщение об ошибке создается компилятором.
Трансляции выражений error и undefined для явного указания мест, где могли возникнуть ошибки времени выполнения. Реальное поведение программы в случае возникновения ошибки зависит от реализации. Сообщения, передаваемые в функцию error при этих трансляциях, являются лишь указаниями, реализации могут выбирать между отображением большей или меньшей информации в случае возникновения ошибки.
aexp |
-> |
qvar |
(переменная) |
| | | gcon |
||
| | | |
(общий конструктор) | |
gcon |
-> |
() | |
| | | [] | ||
| | | (,{,}) | ||
| | | qcon |
||
var |
-> |
varid | ( varsym ) |
(переменная) |
qvar |
-> |
qvarid | ( qvarsym ) |
(квалифицированная переменная) |
con |
-> |
conid | ( consym ) |
(конструктор) |
qcon |
-> |
qconid | ( gconsym ) |
(квалифицированный конструктор) |
varop |
-> |
varsym | 'varid ' |
(оператор переменной) |
qvarop |
-> |
qvarsym | 'qvarid ' |
(квалифицированный оператор переменной) |
conop |
-> |
consym | 'conid ' |
(оператор конструктора) |
qconop |
-> |
gconsym | 'qconid ' |
(квалифицированный оператор конструктора) |
|
-> |
varop | conop |
(оператор) |
qop |
-> |
qvarop | qconop |
(квалифицированный оператор) |
gconsym |
-> |
: | qconsym |
Перевод:
| выражение-аргумента | -> |
квалифицированная-переменная | (переменная) |
| | | общий-конструктор | (общий конструктор) | |
| | | литерал | ||
| общий-конструктор | -> |
() | |
| | | [] | ||
| | | (,{,}) | ||
| | | квалифицированный-конструктор | ||
| переменная | -> |
идентификатор-переменной | ( символ-переменной ) | (переменная) |
| квалифицированная-переменная | -> |
квалифицированный-идентификатор-переменной | ( квалифицированный-символ-переменной ) | (квалифицированная переменная) |
| конструктор | -> |
идентификатор-конструктора | ( символ-конструктора ) | (конструктор) |
| квалифицированный-конструктор | -> |
квалифицированный-идентификатор-конструктора | ( символ-общего-конструктора ) | (квалифицированный конструктор) |
| оператор-переменной | -> |
символ-переменной | 'идентификатор-переменной ' | (оператор переменной) |
| квалифицированный-оператор-переменной | -> |
квалифицированный-символ-переменной | 'квалифицированный-идентификатор-переменной ' | (квалифицированный оператор переменной) |
| оператор-конструктора | -> |
символ-конструктора | 'идентификатор-конструктора ' | (оператор конструктора) |
| квалифицированный-оператор-конструктора | -> |
символ-общего-конструктора | 'квалифицированный-идентификатор-конструктора ' | (квалифицированный оператор конструктора) |
| оператор | -> |
оператор-переменной | оператор-конструктора | (оператор) |
| квалифицированный-оператор | -> |
квалифицированный-оператор-переменной | квалифицированный-оператор-конструктора | (квалифицированный оператор) |
| символ-общего-конструктора | -> |
: | квалифицированный-символ-конструктора |
Для поддержки инфиксной записи в
Оператор представляет собой символ оператора, например, + или $$, или обычный идентификатор, заключенный в обратные кавычки, например, '. Вместо префиксной записи можно использовать инфиксную запись x '. Если ассоциативность и приоритет для ' не заданы, то по умолчанию используется наивысший приоритет и левоассоциативность (см. раздел 4.4.2).
Наоборот, символ оператора может быть преобразован в обычный идентификатор, если записать его в круглых скобках. Например, (+) x y эквивалентно x + y, а foldr (*) 1 xs эквивалентно foldr (\x y -> x*y) 1 xs.
Для обозначения некоторых конструкторов
Целый литерал представляет собой применение функции fromInteger к соответствующему значению типа Integer. Аналогично, литерал с плавающей точкой обозначает применение функции fromRational к значению типа Rational (то есть Ratio Integer ).
Трансляция:
Целый литерал i эквивалентен fromInteger i, где fromInteger - метод класса Num (см. раздел 6.4.1).
Литерал с плавающей точкой f эквивалентен fromRational (n Ratio.% d), где fromRational - метод класса составляет из двух целых чисел Ratio. Если заданы целые числа n и d, то n/d = f.
fexp |
-> |
[fexp] aexp |
(применение функции) |
exp |
-> |
\ apat_1 ...; apat_n -> exp |
(лямбда-абстракция, n >=1 ) |
Перевод:
| функциональное-выражение | -> |
[функциональное-выражение] выражение-аргумента | (применение функции) |
| выражение | -> |
\ такой-как-образец1 ... такой-как-образецn -> выражение |
(лямбда-абстракция, n >= 1 ) |
Применение функции записывается в виде e1 e2. Применение левоассоциативно, поэтому в (f x) y скобки можно опустить. Поскольку e1 может являться конструктором данных, возможно частичное применение конструкторов данных.
Лямбда-абстракции записываются в виде \ p1 ... pn -> e, где pi - образцы. Выражение вида \x:xs -> x синтаксически неправильно, его можно правильно записать в виде
\(x:xs) -> ;x.
Набор образцов должен быть линейным: ни одна переменная не должна появляться в наборе более одного раза.
Трансляция:
Выполняются следующие
\ p1 ... pn -> e=\ x1 ... xn -> case (x1, ... , xn) of (p1, ..., pn) -> e
где xi - новые идентификаторы.
Используя эту трансляцию в комбинации с с семантикой _|_.
exp |
-> |
exp1 qop exp2 |
|
| | | - exp |
(префиксное отрицание) | |
qop |
-> |
qvarop | qconop |
(квалифицированный оператор) |
Перевод:
| выражение | -> |
выражение1 квалифицированный-оператор выражение2 | |
| | | - выражение | (префиксное отрицание) | |
| квалифицированный-оператор | -> |
квалифицированный-оператор-переменной | квалифицированный-оператор-конструктора | (квалифицированный оператор) |
Форма e1 qop e2 представляет собой инфиксное применение бинарного оператора qop к выражениям e1 и e2.
Специальная форма -e обозначает префиксное отрицание, единственный (e). , определенную в Prelude. Нет никакой связи между локальным значением оператора - и унарным отрицанием.
e1-e2 при разборе интерпретируется как инфиксное применение бинарного оператора -, для альтернативной интерпретации нужно писать e1(-e2). Аналогично, (-) является синтаксической записью (\ x y -> x-y), как и любой инфиксный оператор, и не обозначает (\ x -> -x) - для этого нужно использовать
Трансляция:
Выполняются следующие
e1
aexp |
-> |
( expi+1 qop(a,i) ) |
(левое сечение) |
| | | ( lexpi qop(l,i) ) |
(левое сечение) | |
| | | ( qop(a,i)<> expi+1 ) |
(правое сечение) | |
| | | ( qop(r,i)< -> < -> rexpi ) |
(правое сечение) |
Перевод:
| выражение-аргумента | -> |
( выражениеi+1 квалифицированный-оператор(a,i) ) | (левое сечение) |
| | | ( левое-сечение-выраженияi квалифицированный-оператор(l,i) ) | (левое сечение) | |
| | | ( квалифицированный-оператор(a,i)<-> выражениеi+1 ) | (правое сечение) | |
| | | ( квалифицированный-оператор(r,i)<-> правое-сечение-выраженияi ) | (правое сечение) |
Сечения записываются в виде ( или ( e , где - e - выражение. Сечения представляют собой удобный синтаксис для записи частичного применения
Синтаксические правила приоритетов применяются к сечениям следующим образом. ( допустимо, если и только если (x при разборе интерпретируется так же, как и (x ; аналогично для (e . Например, (*a+b) синтаксически недопустимо, но (+a*b) и (*(a+b)) допустимы. Поскольку оператор (+) левоассоциативен, (a+b+) синтаксически правильно, а (+a+b) - нет, его можно правильно записать в виде (+(a+b)). В качестве другого примера рассмотрим выражение
(let n = 10 in n +)
которое является недопустимым в соответствии с мета-правилом для let/лямбда (раздел 3). Выражение
(let n = 10 in n + x)
при разборе интерпретируется как
(let n = 10 in (n + x))
вместо
((let n = 10 in n) + x)
Поскольку - интерпретируется в грамматике специальным образом, (- exp) является не сечением, а применением , определенная в Prelude таким образом, что ( эквивалентно недопустимому ранее сечению. Для той же цели может служить выражение (+ (- exp)).
Трансляция:
Выполняются следующие
(
(e
где - e - выражение, а x - переменная, которая не является свободной в e.
exp |
-> |
if exp1 then exp2 else exp3 |
Перевод:
| выражение | -> |
if выражение1 then выражение2 else выражение3 |
Условное выражение имеет вид if e1 then e2 else e3 и возвращает: значение e2 - если значение e1 равно True, e3 - если e1 равно False, и _|_ - иначе.
Трансляция:
Выполняются следующие
if e1 then e2 else e3= case e1 of { True -> e2 ; False -> e3 }
где True и False - конструкторы с нулевым числом аргументов из типа Bool, определенные в Prelude. Тип e1 должен быть Bool, e2 и e3 должны иметь тот же тип, который также является типом всего условного выражения.
exp |
-> |
exp1 qop exp2 |
|
aexp |
-> |
[ exp1 , ... , expk ] |
(k>=1) |
| | | gcon |
||
gcon |
-> |
[] | |
| | | qcon |
( gconsym ) |
|
qcon |
-> |
||
qop |
-> |
qconop |
|
qconop |
-> |
gconsym |
|
gconsym |
-> |
: |
Перевод:
| выражение | -> |
выражение1 квалифицированный-оператор выражение2 | |
| выражение-аргумента | -> |
[ выражение1 , ... , выражениеk ] | (k >= 1) |
| | | общий-конструктор | ||
| общий-конструктор | -> |
[] | |
| | | квалифицированный-конструктор | ||
| квалифицированный-конструктор | -> |
( символ-общего-конструктора ) | |
| квалифицированный-оператор | -> |
квалифицированный-оператор-конструктора | |
| квалифицированный-оператор-конструктора | -> |
символ-общего-конструктора | |
| символ-общего-конструктора | -> |
: |
Списки записываются в виде [e1, ..., ek], где k >= 1. Конструктором списка является :, пустой список обозначается []. Стандартные операции над списками описаны в Prelude (см. раздел 6.1.3 и лекции 8, особенно раздел 8.1).
Трансляция:
Выполняются следующие
[e1, ..., ek]= e1 : (e2 : ( ... (ek : [])))
где : и [] - конструкторы списков, определенные в Prelude (см. раздел 6.1.3). Выражения e1, .... , ek должны быть одного типа (назовем его t ), а типом всего выражения является [t] (см. раздел 4.1.2).
Конструктор : предназначен исключительно для построения списка; как и [], он является обоснованной частью синтаксиса языка и не может быть скрыт или переопределен. Он представляет собой правоассоциативный оператор с уровнем приоритета 5 (раздел 4.4.2).
aexp |
-> |
( exp1 , ... , expk ) |
(k>=2) |
| | | qcon |
||
qcon |
-> |
(,{,}) |
Перевод:
| выражение-аргумента | -> |
( выражение1 , ... , выражениеk ) | (k>=2) |
| | | квалифицированный-конструктор | ||
| квалифицированный-конструктор | -> |
(,{,}) |
Кортежи записываются в виде (e1, ..., ek) и могут быть произвольной длины k >= 2. Конструктор для кортежа размера n обозначается (,...,), где n-1 запятых. Таким образом, (a,b,c) и (,,) a b c обозначают одно и то же значение. Стандартные операции над кортежами описаны в Prelude (см. раздел 6.1.4 и лекцию 8).
Трансляция:
(e1, ..., ek) для k >= 2 является экземпляром кортежа размера k, в соответствии с определением в Prelude, и не требует трансляции. Если t1, ... , tk - соответственно типы e1, ... , ek, то типом кортежа будет (t1, ..., tk) (см. раздел 4.1.2).
aexp |
-> |
gcon |
| | | ( exp ) |
|
gcon |
-> |
() |
Перевод:
| выражение-аргумента | -> |
общий-конструктор |
| | | ( выражение ) | |
| общий-конструктор | -> |
() |
Выражение вида (e) представляет собой просто выражение в скобках и эквивалентно e. Единичное выражение () имеет тип () (см. раздел 4.1.2). Этот единственный член указанного типа, отличный от _|_, может рассматриваться как "кортеж нулевого размера" (см. раздел 6.1.5).
Трансляция:
(e) эквивалентно e
aexp |
-> |
[ exp1 [, exp2] .. [exp3] ] |
Перевод:
| выражение-аргумента | -> |
[ выражение1 [, выражение2] .. [выражение3] ] |
Арифметическая последовательность [e1, e2 .. e3] обозначает список значений типа t, где каждое из выражений ei имеет тип t, и t является экземпляром класса Enum.
Трансляция:
Для арифметических последовательностей выполняются следующие
[ e1 ... ] = enumFrom e1
[ e1,e2 ... ] = enumFromThen e1 e2
[ e1 ... e3 ] = enumFromTo e1 e3
[ e1,e2 ... e3 ]= enumFromThenTo e1 e2 e3
где enumFrom, enumFromThen, enumFromTo и enumFromThenTo являются методами класса Enum, определенные в Prelude (см. раздел 6.3.2).
Семантика арифметических последовательностей поэтому полностью зависит от объявления экземпляра для типа t. Для получения более детальной информации о том, какие типы Prelude являются подтипами Enum и какова их семантика, смотрите рздел 6.3.4.
aexp |
-> |
[ exp | qual1 , ... , qualn ] |
(описание списка, n >= 1) |
qual |
-> |
|
(генератор) |
| | | let decls |
(локальное объявление) | |
| | | exp |
(страж) |
Перевод:
| выражение-аргумента | -> |
[ выражение | квалификатор1 , ... , квалификаторn ] | (описание списка, n>=1 ) |
| квалификатор | -> |
образец <- выражение | (генератор) |
| | | let списки-объявлений | (локальное объявление) | |
| | | выражение | (страж) |
Описание списка имеет вид [ e | q1, ..., qn ], n>=1, где квалификаторы qi являются
p 7 >= e, где p - образец (см. раздел 3.17) типа t, а e - выражение типа [t],Такое описание списка возвращает список элементов, порожденный путем вычисления e в последовательных окружениях, созданных вложенным вычислением вглубину генераторов в списке квалификаторов. Связывание имен переменных происходит согласно правилам обычного сопоставления с образцом (см. раздел 3.17), и если сопоставление завершится неудачей, то соответствующий элемент списка будет просто пропущен. Таким образом,
[ x | xs <- [ [(1,2),(3,4)], [(5,4),(3,2)] ],
(3,x) <- xs ]
порождает список [4,2]. Если квалификатор является стражем, то, для того чтобы предшествующее сопоставление с образцом завершилось успешно, необходимо, чтобы значение квалификатора равнялось True. Как обычно, связывания имен в описаниях списков могут скрыть связывания имен во внешних областях видимости, например,
[ x | x <- x, x <- x ]= [ z | y <- x, z <- y]
Трансляция:
Для описаний списков выполняются следующие
[ e | True ] = [e]
[ e | q ] = [ e | q, True ]
[ e | b, Q ] = if b then [ e | Q ] else []
[ e | p <- l, Q ] = let ok p = [ e | Q ]
ok _ = []
in concatMap ok l
[ e | let decls, Q ]= let decls in [ e | Q ]
где e - выражения, p - образцы, l - выражения, значениями которых являются списки, b - булевы выражения, decls - списки объявлений, q - квалификаторы, а Q - последовательности квалификаторов. ok - новая переменная. Функция concatMap и булево значение True определены в Prelude.
Как показывает трансляция описаний списков, переменные, связанные с помощью let, имеют полностью полиморфные типы, тогда как переменные, определенные с помощью >-, связаны лямбда-выражением и поэтому мономорфны (см. раздел 4.5.4).
exp |
-> |
let decls in exp |
Перевод:
| выражение | -> |
let списки-объявлений in выражение |
Let-выражения имеют общий вид let { d1 ; ... ; dn } in e и вводят вложенный, лексически ограниченный, взаимно рекурсивный список объявлений (в других языках let часто называют letrec ). Областью видимости объявлений является выражение e и правая часть объявлений. Объявления описаны в лекции 4. Сопоставление и связывание образцов выполняется лениво, неявная - делает эти образцы неопровержимыми. Например,
let (x,y) = undefined in e
не вызовет ошибку времени выполнения до тех пор, пока x или y не будут вычислены.
Трансляция:
Динамическая семантика выражения let { d1 ; ... ; dn } в e0 охватывается следующей трансляцией: после удаления всех сигнатур типов каждое объявление di транслируется в уравнение вида pi = ei, где pi и ei - соответственно образцы и выражения, при этом используется трансляция в разделе 4.4.3. Однажды сделав это, эти
let {p1=e1; ... ; pn=e1} in e0 = let (~p1, ... ,~pn) = (e1, ... ,en) in e0
let p = e1 in e0 = case e1 of ~p -> e0
где ни одна переменная в p не является свободной в e1
let p = e1 in e0 = let p = fix ( \ ~p -> e1) in e0
где fix - наименьший ассоциативный оператор. Обратите внимание на использование неопровержимого образца ~ p. Эта трансляция не сохраняет статическую семантику, потому что использование case препятствует полностью
exp |
-> |
case exp of { alts } |
|
alts |
-> |
alt1 ; ... ; altn |
(n>=1) |
alt |
-> |
|
|
| | | |
||
| | | (пустая альтернатива) | ||
gdpat |
-> |
gd -> exp [ gdpat ] |
|
gd |
-> |
| exp0 |
Перевод:
| выражение | -> |
case выражение of { список-альтернатив | |
| список-альтернатив | -> |
альтернатива1 ; ... ; альтернативаn | (n>=1) |
| альтернатива | -> |
образец -> выражение [where список-объявлений] |
|
| | | образец образец-со-стражами [where список-объявлений] | ||
| | | (пустая альтернатива) | ||
| образец-со-стражами | -> |
страж -> выражение [ образец-со-стражами ] |
|
| страж | -> |
| выражение0 |
case e of { p1 match1 ; ... ; pn matchn }
где каждый matchi имеет общий вид
| gi1-> ei1 ... | gimi-> eimi where declsi
(Заметьте, что в синтаксическом правиле для gd "|" является pi matchi состоит из образца pi и его сопоставлений matchi. Каждое сопоставление, в свою очередь, состоит из последовательности пар стражей gij и тел eij (выражений), за которыми следуют необязательные связывания (declsi), чья область видимости распространяется над всеми стражами и выражениями альтернативы. Альтернатива вида
pat -> exp where decls
интерпретируется как краткая запись для
pat | True-> exp where decls
Вычисление e отдельным альтернативам. Альтернативы проверяются последовательно, сверху вниз. Если e соответствует образцу в альтернативе, выполняется связывание переменных, сначала указанных в образце, а затем - с помощью declsi в операторе where, связанном с этой альтернативой. Если значение одного из вычисляемых стражей окажется True, в том же окружении, что и страж, будет вычислена соответствующая правая часть. Если значения всех стражей окажутся False, процесс сопоставления с образцом будет возобновлен со следующей альтернативы. Если не удастся сопоставить ни один образец, результатом будет _|_. Сопоставление с образцом описано в разделе 3.17, а формальная семантика
Замечание о разборе. Выражение
case x of { (a,_) | let b = not a in b :: Bool -> a }
нелегко правильно интерпретировать при разборе. Оно имеет единственную однозначную интерпретацию, а именно:
case x of { (a,_) | (let b = not a in b :: Bool) -> a }
Тем не менее, выражение Bool -> a является синтаксически правильным типом, и синтаксические анализаторы с ограниченным предварительным просмотром могут выбрать этот неправильный вариант, и тогда программа будет признана недопустимой. Поэтому мы советуем программистам избегать использования стражей, которые заканчиваются указанием сигнатуры типа, именно поэтому gd содержит exp0, а не не exp.
exp |
-> |
do { stmts } |
(do-выражение) |
stmts |
-> |
stmt1 ... stmtn exp [;] |
(n>=0) |
stmt |
-> |
exp ; |
|
| | | |
||
| | | let decls ; |
||
| | | ; | (пустая инструкция) |
Перевод:
| выражение | -> |
do { список-инструкций } | (do-выражение) |
| список-инструкций | -> |
инструкция1 ... инструкцияn выражение [;] | (n>=0) |
| инструкция | -> |
выражение ; | |
| | | образец \gets выражение ; | ||
| | | let список-объявлений ; | ||
| | | ; | (пустая инструкция) |
Do-выражения предоставляют более удобный синтаксис для монадического программирования. Оно позволяет записать такое выражение
putStr "x: " >> getLine >>= \l -> return (words l)
в более традиционном виде:
do putStr "x: "
l <- getLine
return (words l)
Трансляция:
Для do-выражений выполняются следующие
do {e} = e
do {e;stmts} = e >> do {stmts}
do {p <- e; stmts} = let ok p = do {stmts}
ok _ =
in e >>= ok
do {let decls; stmts} = let decls in do {stmts}
Пропуски "..." обозначают генерируемое компилятором сообщение об ошибке, передаваемое функции , желательно давая некоторое указание на местоположение ошибки сопоставления с образцом; функции >>, >>= и являются операциями в классе ok является новым идентификатором.
Как показано в трансляции do, переменные, связанные let, имеют полностью полиморфные типы, тогда как те переменные, которые определены с помощью <-, являются связанными лямбда-выражением и поэтому являются мономорфными.
Объявление типа данных может содержать необязательные определения имен полей (см. раздел 4.2.1). Эти имена полей можно использовать для создания, извлечения и обновления полей способом, который не зависит от всей структуры типа данных.
Различные типы данных не могут совместно использовать общие имена полей в одной области видимости. Имя поля можно использовать не более одного раза в конструкторе. В пределах типа данных, тем не менее, имя поля можно использовать в более чем одном конструкторе, при условии, что поле имеет один и тот же тип во всех конструкторах. Для того чтобы проиллюстрировать последнее замечание, рассмотрим:
data S = S1 { x :: Int } | S2 { x :: Int } - OK
data T = T1 { y :: Int } | T2 { y :: Bool } - ПЛОХО
Здесь S является допустимым типом данных, а T - нет, потому что в последнем случае для y указан другой тип, противоречащий указанному ранее.
aexp |
-> |
qvar |
Перевод:
| выражение-аргумента | -> |
квалифицированная-переменная |
Имена полей используются в качестве селекторной функции. Когда имя поля используется в качестве переменной, оно действует как функция, которая извлекает поле из объекта. Селекторы являются связываниями верхнего уровня, и поэтому они могут быть перекрыты локальными переменными, но не могут конфликтовать с другими связываниями верхнего уровня с тем же именем. Это сокрытие затрагивает только селекторные функции, при создании записей (раздел 3.15.2) и их обновлении (раздел 3.15.3) имена полей не могут быть спутаны с обычными переменными.
Трансляция:
Имя поля f представляет собой селекторную функцию в соответствии с определением:
f x = case x of { C1 p11... p1k -> e1; ... ; Cn pn1 ... pnk -> en }
где все C1 ...Cn - f, pij - это y, когда f именует собой j -ю компоненту Ci, или _ иначе, а ei - это y, когда некоторое поле в Ci имеет имя f, или undefined иначе..
aexp |
-> |
qcon { fbind1 , ... , fbindn } |
(именованная конструкция, n>=0 ) |
fbind |
-> |
qvar = exp |
Перевод:
| выражение-аргумента | -> |
квалифицированный-конструктор { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованная конструкция, n>=0 ) |
| связывание-имени-поля | -> |
квалифицированная-переменная = выражение |
Конструктор с именованными полями может использоваться для создания значения, в котором компоненты задаются именем, а не позицией. В отличие от фигурных скобок, используемых в списках объявлений, здесь присутствие фигурных скобок не зависит от размещения текста, символы { и } должны использоваться явно. (То же самое относится к обновлению полей и образцам полей.) Создание объектов с использованием имен полей подчинено следующим ограничениям:
_|_.Выражение F {}, где F - конструктор данных, является допустимым независимо от того, было или нет F объявлено с использованием синтаксиса записи (при условии, что F не имеет обязательных полей, см. пункт третий в приведенном выше списке); оно обозначает F _|_1 ... _|_n, где n - число аргументов F.
Трансляция:
В связывании f = v поле f именует v.
C { bs } = C (
где k - число аргументов C.
Вспомогательная функция определена следующим образом:
Если i-ый компонент конструктора C имеет имя поля f и если f=v появляется в списке связываний bs, то равно v. Иначе равно значению по умолчанию d.
aexp |
-> |
aexp<qcon> { fbind1 , ... , fbindn } |
(именованное обновление, n>=1 ) |
Перевод:
| выражение-аргумента | -> |
выражение-аргумента<квалифицированный-конструктор> { связывание-имени-поля1 , ... , связывание-имени-поляn } | (именованное обновление, n>=1 ) |
Значения, принадлежащие типу данных с именованными полями, можно обновлять, не боясь разрушить структуру данных. При этом создается новое значение, в котором заданные значения полей замещают те, что были в существующем значении. Обновления подчиняются следующим правилам:
Трансляция:
Используя предыдущее определение функции ,
e { bs }= case e of
C1 v1 ... vk1 -> C1 (
…
Cj v1 ... vkj -> Cj (
-> error "Ошибка обновления"
где {C1,...,Cj} - набор конструкторов, содержащих все имена в bs, а ki - число аргументов Ci.
Вот некоторые примеры, использующие именованные поля:
data T = C1 {f1,f2 :: Int}
| C2 {f1 :: Int,
f3,f4 :: Char}
| Выражение | Трансляция |
|---|---|
C1 {f1 = 3} |
C1 3 undefined |
C2 {f1 = 1, f4 = 'A', f3 = 'B'} |
C2 1 'B' 'A' |
x {f1 = 1} |
case x of C1 _ f2 -> C1 1 f2 |
C2 _ f3 f4 -> C2 1 f3 f4 |
Поле f1 является общим для обоих конструкторов в T. Этот пример транслирует выражения, использующие конструкторы, записываемые с именами полей, в x {f2 = 1, f3 = 'x'}, произойдет ошибка компиляции.
exp |
-> |
exp :: [context =>] type |
Перевод:
| выражение | -> |
выражение :: [контекст => ] тип |
Сигнатуры типов выражений имеют вид e :: t, где e - выражение, а t - тип (раздел 4.1.2); они используются для явного указания типа выражения, в частности, для того чтобы разрешить неоднозначность типов из-за перегрузки (см. раздел 4.3.4). Значением выражения является значение exp. Как и с обычными сигнатурами типов (см. раздел 4.4.1), объявленный тип может быть более частным, чем основной тип, выводимый из exp, но будет ошибкой указать тип, который окажется более общим или не сопоставимым с основным типом.
Трансляция:
e :: t = let { v :: t; v = e } in v
Образцы появляются в лямбда-абстракциях, определениях функций, связываниях с образцом, описаниях списков, do-выражениях и case-выражениях. Тем не менее, первые пять из них в конечном счете транслируются в
Образцы имеют следующий синтаксис:
|
-> |
var + integer |
(образец упорядочивания) |
| | | |
||
|
-> |
|
|
| | | lpati |
||
| | | rpati |
||
lpati |
-> |
(lpati | |
|
lpat6 |
-> |
- (integer | float) |
(отрицательный литерал) |
rpati |
-> |
|
|
|
-> |
apat |
|
| | | gcon apat1 ... apatk |
(число аргументов конструктора gcon = k, k >= 1 ) |
|
apat |
-> |
var [@ apat] |
("такой как"-образец) |
| | | gcon |
(число аргументов конструктора gcon = 0 ) |
|
| | | qcon { fpat1 , ... , fpatk } |
(именованный образец, k >= 0 ) |
|
| | | |
||
| | | _ | (любые символы) | |
| | | ( |
(образец в скобках) | |
| | | ( |
(образец кортежа, k >= 2 ) |
|
| | | [ |
(образец списка, k >= 1 ) |
|
| | | ~apat |
(неопровержимый образец) | |
fpat |
-> |
qvar = |
Перевод:
| образец | -> |
переменная + целый-литерал | (образец упорядочивания) |
| | | образец0 | ||
| образецi | -> |
образецi+1 [квалифицированный-оператор-конструктора(n,i) образецi+1] | |
| | | левый-образецi | ||
| | | правый-образецi | ||
| левый-образецi | -> |
(левый-образецi | образецi+1) квалифицированный-оператор-конструктора(l,i) образецi+1 | |
| левый-образец6 | -> |
- (целый-литерал | литерал-с-плавающей-точкой) | (отрицательный литерал) |
| правый-образецi | -> |
образецi+1 квалифицированный-оператор-конструктора(r,i) (правый-образецi | образецi+1) | |
| образец10 | -> |
такой-как-образец | |
| | | общий-конструктор такой-как-образец1 ... такой-как-образецk | (число аргументов конструктора gcon = k, k >= 1 ) |
|
| такой-как-образец | -> |
переменная [@ такой-как-образец] | ("такой как"-образец) |
| | | общий-конструктор | (число аргументов конструктора gcon = 0) | |
| | | квалифицированный-конструктор { образец-с-именем1 , ... , образец-с-именемk } | (именованный образец, k >= 0 ) |
|
| | | литерал | ||
| | | _ | (любые символы) | |
| | | ( образец ) | (образец в скобках) | |
| | | ( образец1 , ... , образецk ) | (образец кортежа, k >= 2 ) |
|
| | | [ образец1 , ... , образецk ] | (образец списка, k>=1 ) |
|
| | | ~ такой-как-образец |
(неопровержимый образец) | |
| образец- | -> |
квалифицированная-переменная = образец |
Число аргументов конструктора должно соответствовать числу образцов, связанных с ним; нельзя сопоставлять частично примененный конструктор.
Все образцы должны быть линейными : ни одна переменная не может появляться более одного раза. Например, следующее определение недопустимо:
f (x,x) = x - ЗАПРЕЩЕНО; x дважды используется в образце
Образцы вида var@ называются "такими как" - образцами, и позволяют использовать var в качестве имени для значения, сопоставляемого . Например,
case e of { xs@(x:rest) -> if x==0 then rest else xs }
эквивалентено
let { xs = e } in
case xs of { (x:rest) -> if x==0 then rest else xs }
Образцы вида _ обозначают группы любых символов и полезны, когда некоторая часть образца не используется в правой части. Это как если бы идентификатор, не используемый где-либо в другом месте, был помещен на свое место. Например,
case e of { [x,_,_] -> if x==0 then True else False }
эквивалентно
case e of { [x,y,z] -> if x==0 then True else False }
Образцы сопоставляются значениям. Попытка сопоставить образец может иметь один из трех результатов: потерпеть неудачу, иметь успех, при этом каждая переменная в образце связывается с соответствующим значением, или быть отклонена (т.е. вернуть _|_). Сопоставление с образцом выполняется слева направо и извне вовнутрь, в соответствии со следующими правилами:
var значению v всегда имеет успех и связывает var с v.~apat значению v всегда имеет успех. apat связываются с соответствующими значениями, если сопоставление apat с v завершится успешно, или с _|_, если сопоставление apat с v потерпит неудачу или будет отклонено. (Связывание не подразумевает вычисление.)С точки зрения операций, это означает, что никакое сопоставление не будет сделано с образцом ~apat до тех пор, пока одна из переменных в apat не будет использована. В этот момент весь образец сопоставляется значению, и если сопоставление потерпит неудачу или будет отклонено, так выполняется все вычисление.
con pat значению, где con - конструктор, определенный с помощью newtype, зависит от значения:con v, то pat сопоставляется v._|_, то pat сопоставляется _|_.То есть конструкторы, связанные с newtype, служат только для того, чтобы изменить тип значения.
con pat 1 ...pat n значению, где con - конструктор, определенный с помощью data, зависит от значения:con v1 ...vn, части образца сопоставляются слева направо компонентам значения данных, если все сопоставления завершатся успешно, результатом всего сопоставления будет успех; первая же неудача или отклонение приведут к тому, что сопоставление с образцом соответственно потерпит неудачу или будет отклонено.con' v1 ...vm, где con - конструктор, отличный от con', сопоставление потерпит неудачу._|_, сопоставление будет отклонено.k
значениюv имеет успех, если v == k, где == перегружен на основании типа образца. Сопоставление будет отклонено, если эта проверка будет отклонена.Интерпретация числовых литералов в точности описана в разделе 3.2, то есть fromInteger или fromRational применяется к литералу типа Integer или Rational (соответственно) для преобразования его к соответствующему типу.
n+k -образца (где n - переменная, а k - положительный целый литерал) значению v имеет успех, если x >= k, при этом n связывается с x - k, и терпит неудачу иначе. Снова, функции >= и - являются перегруженными в зависимости от типа образца. Сопоставление будет отклонено, если сравнение будет отклонено.Интерпретация литерала k является точно такой же, как и для числовых литералов, за исключением того, что допустимы только целые литералы.
var@apat значению v является результатом сопоставления apat с v, дополненного связыванием var с v. Если сопоставление apat с v потерпит неудачу или будет отклонено, такой же результат будет у сопоставления с образцом.Помимо очевидных ограничений
Num.Fractional .n+k -образец можно сопоставить только значению в классе Integral.Многие люди считают, что n+k -образцы не следует использовать. Эти образцы могут быть удалены или изменены в будущих версиях
Иногда полезно различать два вида образцов. Сопоставление с неопровержимым образцом не является строгим: образец сопоставляется, даже если сопоставляемое значение равно _|_ _. Сопоставление с опровержимым образцом является строгим: если сопоставляемое значение равно _|_сопоставление будет отклонено. Неопровержимыми являются следующие образцы: переменная, символ подчеркивания, N apat, где N - конструктор, определенный с помощью newtype, а apat - неопровержимый образец (см. раздел 4.2.3), var@apat, где apat - неопровержимый образец, и образцы вида ~apat (независимо от того, является ли apat неопровержимым образцом или нет). Все остальные образцы являются опровержимыми.
Приведем несколько примеров:
['a','b'] сопоставляется ['x' _|_, ], то сопоставление 'a' с 'x' потерпит неудачу, и все сопоставление завершится неудачей. Но если ['a','b'] сопоставляется [_|_ ,'x'], то попытка сопоставить 'a' с _|_ приведет к тому, что все сопоставление будет отклонено.(\ ~(x,y) -> 0) _|_ => 0 (\ (x,y) -> 0) _|_ => _|_ (\ ~[x] -> 0) [] => 0 (\ ~[x] -> x) [] => _|_ (\ ~[x,~(a,b)] -> x) [(0,1),_|_] => (0,1) (\ ~[x, (a,b)] -> x) [(0,1),_|_] => _|_ (\ (x:xs) -> x:x:xs) _|_ => _|_ (\ ~(x:xs) -> x:x:xs) _|_ => _|_:_|_:_|_
newtype N = N Bool data D = D !Bool
Эти примеры показывают различие между типами, определенными с помощью data и newtype, при сопоставлении с образцом:
(\ (N True) -> True) _|_ => _|_ (\ (D True) -> True) _|_ => _|_ (\ ~(D True) -> True) _|_ => True
Дополнительные примеры вы найдете в разделе 4.2.3.
Образцы верхнего уровня в case-выражениях и набор образцов верхнего уровня в связываниях имен функций или образцах могут иметь ноль или более связанных с ними стражей. Страж - это булево выражение, которое вычисляется только после того, как все аргументы были успешно сопоставлены, и, для того чтобы все сопоставление с образцом имело успех, значением этого выражения должно быть истина. Окружением стража является то же окружение, что и для правой части альтернативы
Семантика стража имеет очевидное влияние на характеристики строгости функции или
f :: (Int,Int,Int) -> [Int] -> Int f ~(x,y,z) [a] | (a == y) = 1
и a, и y будут вычислены с помощью == в страже.
Семантика всех конструкций сопоставления с образцом, отличных от case -выражений, определена с помощью case -выражениями. В свою очередь, семантика самих
(a) case e of { alts } = (\v -> case v of { alts }) e
где v - новая переменная
(b) case v of { p1 match1; ... ; pn matchn }
= case v of { p1 match1 ;
_ -> ... case v of {
pn matchn ;
_ -> error "Нет сопоставлений" }...}
где каждое matchi имеет вид:
| gi,1 -> ei,1 ; ... ; | gi,mi -> ei,mi where { declsi }
(c) case v of { p | g1 -> e1 ; ...
| gn -> en where { decls }
_ -> e' }
= case e' of
{y -> (где y - новая переменная)
case v of {
p -> let { decls } in
if g1 then e1 ... else if gn then en else y ;
_ -> y }}
(d) case v of { ~p -> e; _ -> e' }
= (\x1 ... xn -> e ) (case v of { p-> x1 }) ... (case v of { p -> xn})
где x1, ..., xn - переменные в p
(e) case v of { x@p -> e; _ -> e' }
= case v of { p -> ( \ x -> e ) v ; _ -> e' }
(f) case v of { _ -> e; _ -> e' } = e
(g) case v of { K p1 ...pn -> e; _ -> e' }
= case v of {
K x1 ...xn -> case x1 of {
p1 -> ... case xn of { pn -> e ; _ -> e' } ...
_ -> e' }
_ -> e' }
по меньшей мере один из p1, ..., pn не является переменной; x1, ..., xn - новые переменные
(h) case v of { k -> e; _ -> e' } = if (v==k) then e else e'
где k - числовой, символьный или строковый литерал
(i) case v of { x -> e; _ -> e' } = case v of { x -> e }
(j) case v of { x -> e } = ( \ x -> e ) v
(k) case N v of { N p -> e; _ -> e' }
= case v of { p -> e; _ -> e' }
где N - конструктор newtype
(l) case _|_ of { N p -> e; _ -> e' } = case _|_ of { p -> e }
где N - конструктор newtype
(m) case v of { K { f1 = p1 , f2 = p2 , ... } -> e ; _ -> e' }
= case e' of {
y ->
case v of {
K { f1 = p1 } ->
case v of { K { f2 = p2 , ... } -> e ; _ -> y };
_ -> y }}
где f1, f2, ... - поля конструктора K, y- новая переменная
(n) case v of { K { f = p } -> e ; _ -> e' }
= case v of {
K p1 ... pn -> e ; _ -> e' }
где pi равно p, если f именует i-ую компоненту K, _ иначе
(o) case v of { K {} -> e ; _ -> e' }
= case v of {
K _ ... _ -> e ; _ -> e' }
(p) case (K' e1 ... em) of { K x1 ... xn -> e; _ -> e' } = e'
где K и K' - различные конструкторы data соответственно с n и m аргументами
(q) case (K e1 ... en) of { K x1 ... xn -> e; _ -> e' }
= (\x1 ... xn -> e) e1 ... en
где K - конструктор data с n аргументами
(r) case _|_ of { K x1 ... xn -> e; _ -> e' } = _|_
где K - конструктор data с n аргументами
(s) case v of { x+k -> e; _ -> e' }
= if v >= k then (\x -> e) (v-k) else e'
где k - числовой литерал
На рис. 3.1-3.2: e, e' и ei - выражения, g и gi - булевы выражения, p и pi - образцы, v, x и xi - переменные, K и K' - конструкторы алгебраических типов данных (data) (включая конструкторы кортежей), а N - конструктор newtype.
Правило (b) соответствует основному case -выражению исходного языка, независимо от того, включает ли оно стражей: если стражи не указаны, то в формах matchi вместо стражей gi,j будет подставлено True. Последующие
Правило (h) на 3.2 затрагивает == ; именно это правило определяет смысл сопоставления образца с перегруженными константами.
Все эти (d), (e), (j), (q) и (s) используют лямбду, а не let ; это указывает на то, что переменные, связанные с помощью case, являются мономорфными (раздел 4.1.4).
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.