Опубликован: 19.09.2008 | Уровень: специалист | Доступ: платный
Лекция 4:

Выражения

Аннотация: В этой лекции мы опишем синтаксис и неформальную семантику выражений Haskell , включая, где это возможно, их трансляцию в ядро Haskell
Ключевые слова: свободная переменная, идентификатор, индекс, no-op, переменная, CASE-выражение, literal, операторы, отрицание, префиксный оператор, haskell, инфиксный, грамматика, let, выражение, ассоциативность, синтаксис, значение, вызов функции, функция, сообщение об ошибке, ошибки времени выполнения, запись, встроенные типы, целый, литерал, fraction, рациональное число, тождество, бинарный оператор, определение, переопределенная система, унарный оператор, negation, subtract, false, список, операции, конструктор, кортеж, семантика, PAT, связывание имен , элемент списка, true, трансляция, связывание, полиморфная типизация, связанная переменная, терминальный символ, альтернатива, очередь, область видимости, вычисление, программа, fail, monadic, поле, конструктор типа, picking, эквивалентное выражение, перегруженная функция, статический тип, перегруженный оператор

За исключением let -выражений эти трансляции сохраняют и статическую, и динамическую семантику. Свободные переменные и конструкторы, используемые в этих трансляциях, всегда ссылаются на сущности, определенные в Prelude. Например, "concatMap", используемая в трансляции описания списка (раздел "3.11" ), обозначает concatMap, определенную в Prelude, невзирая на то, находится ли идентификатор "concatMap" в области видимости, где используется описание списка, и (если находится в области видимости) к чему он привязан.

В синтаксисе, который следует далее, есть некоторые семейства нетерминалов, индексированные уровнями приоритета (записанными как верхний индекс). Аналогично, нетерминалы op (оператор), 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 } (case-выражение)
| do { stmts } (do-выражение)
| fexp
fexp -> [fexp] aexp (применение функции)
aexp -> qvar (переменная)
| gcon (общий конструктор)
| literal
| ( 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.

Отрицание является единственным префиксным оператором в Haskell , он имеет тот же приоритет, что и инфиксный оператор -, определенный в 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/лямбда.

Ради ясности остальная часть этого раздела описывает синтаксис выражений без указания их приоритетов.

KroshkaRu KroshkaRu
KroshkaRu KroshkaRu
Россия, Петерубрг, СПБ-ГПУ, 1998
Петр Бондареко
Петр Бондареко
Россия