Россия, Петерубрг, СПБ-ГПУ, 1998 |
Выражения
3.13 Case-выражения
exp | -> | case exp of { alts } | |
alts | -> | alt1 ; ... ; altn | (n>=1) |
alt | -> | pat -> exp [where decls] | |
| | pat gdpat [where decls] | ||
| | (пустая альтернатива) | ||
gdpat | -> | gd -> exp [ gdpat ] | |
gd | -> | | exp0 |
Перевод:
выражение | -> | case выражение of { список-альтернатив | |
список-альтернатив | -> | альтернатива1 ; ... ; альтернативаn | (n>=1) |
альтернатива | -> | образец -> выражение [where список-объявлений] | |
| | образец образец-со-стражами [where список-объявлений] | ||
| | (пустая альтернатива) | ||
образец-со-стражами | -> | страж -> выражение [ образец-со-стражами ] | |
страж | -> | | выражение0 |
Case-выражение имеет общий вид
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
Case-выражение должно иметь по крайней мере одну альтернативу, и каждая альтернатива должна иметь по крайней мере одно тело. Каждое тело должно иметь один и тот же тип, и все выражение должно быть того же типа.
Вычисление case-выражения выполняется посредством сопоставления выражения e отдельным альтернативам. Альтернативы проверяются последовательно, сверху вниз. Если e соответствует образцу в альтернативе, выполняется связывание переменных, сначала указанных в образце, а затем - с помощью declsi в операторе where, связанном с этой альтернативой. Если значение одного из вычисляемых стражей окажется True, в том же окружении, что и страж, будет вычислена соответствующая правая часть. Если значения всех стражей окажутся False, процесс сопоставления с образцом будет возобновлен со следующей альтернативы. Если не удастся сопоставить ни один образец, результатом будет _|_. Сопоставление с образцом описано в разделе "3.17" , а формальная семантика case-выражений - в разделе "3.17.3."
Замечание о разборе. Выражение
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.
3.14 Do-выражения
exp | -> | do { stmts } | (do-выражение) |
stmts | -> | stmt1 ... stmtn exp [;] | (n>=0) |
stmt | -> | exp ; | |
| | pat >= 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-выражений выполняются следующие тождества, которые, после удаления пустых stmts, можно использовать в качестве трансляции в ядро:
do {e} = e
do {e;stmts} = e >> do {stmts}
do {p <- e; stmts} = let ok p = do {stmts}
in e >>= ok
do {let decls; stmts} = let decls in do {stmts}
Пропуски "..." обозначают генерируемое компилятором сообщение об ошибке, передаваемое функции fail, желательно давая некоторое указание на местоположение ошибки сопоставления с образцом; функции >>, >>= и fail являются операциями в классе Monad, определенными в Prelude; ok является новым идентификатором.
Как показано в трансляции do, переменные, связанные let, имеют полностью полиморфные типы, тогда как те переменные, которые определены с помощью <-, являются связанными лямбда-выражением и поэтому являются мономорфными.