Синтаксический сахар
Синтаксический сахар (англ. syntactic sugar) в языке программирования — это синтаксические возможности, применение которых не влияет на поведение программы, но делает использование языка более удобным для человека.
Это может быть любой элемент синтаксиса, который даёт программисту альтернативный способ записи уже имеющейся в языке синтаксической конструкции, и при этом является более удобным, или более кратким, или похожим на другой распространённый способ записи, или помогает писать программы в хорошем стиле.
Определение
Под «синтаксическим сахаром» понимается любой имеющийся в языке программирования синтаксический элемент, механизм, способ описания, который дублирует другой, имеющийся в языке элемент или механизм, но является более удобным в использовании, или более краток, или выглядит естественнее, или более привычен (похож на аналогичные элементы других языков), или просто лучше воспринимается при чтении программы человеком. Принципиально то, что синтаксический сахар, теоретически, всегда можно удалить из языка без потери его возможностей — всё, что можно написать с применением синтаксического сахара, может быть написано на этом же языке и без него. Таким образом, синтаксический сахар предназначен лишь для того, чтобы сделать более удобным для программиста написание программы.
Понятие синтаксического сахара во многом условно. Его использование предполагает, что из всего множества синтаксических конструкций, имеющихся в языке, можно выделить некоторый «базовый набор», обеспечивающий функциональность языка, и дополнительные синтаксические средства, которые при желании можно выразить с помощью базового набора; последние и будут для данного языка синтаксическим сахаром. Однако многие конструкции являются взаимозаменяемыми, и далеко не всегда можно определённо сказать, какие именно из них являются базовыми, а какие — дополнительными. Например, в языке Модула-2 есть четыре вида циклов: цикл с предусловием, цикл с постусловием, цикл с шагом и безусловный цикл. Теоретически, первые три вида циклов могут быть легко выражены через последний. Являются ли они, в таком случае, синтаксическим сахаром? Обычно так не говорят, хотя формально под вышеприведённое определение они попадают.
Отнесение некоторых конструкций к синтаксическому сахару неоднозначно в силу исторических причин. Например, в языке C и его потомках имеются операторы инкремента, декремента и составного присваивания (++
, --
, +=
, -=
и другие). Введение этих операторов в язык было вызвано необходимостью поддержки ручной оптимизации программ, так как код с их использованием мог транслироваться в более эффективные машинные команды («++a
» транслировалось в одну команду INC, а аналогичное выражение «a=a+1
» — в целую группу команд). Развитие технологии оптимизации кода сделало подобную ручную оптимизацию бессмысленной; сейчас компиляторы порождают одинаковый код для «длинного» и «короткого» варианта операции. В результате сокращённые операторы превратились в синтаксический сахар, хотя изначально им не были.
«Синтаксический сахар» или «Лексический мусор»?
В некоторых случаях понятие «синтаксический сахар» трактуют более широко, нежели «другой способ записи для уже имеющихся синтаксических конструкций». Джек Креншоу в книге «Давайте создадим компилятор!»[1] применяет этот термин к синтаксическим элементам, которые не нужны для правильной компиляции программы, а включаются в язык исключительно для обеспечения удобства программиста и для удобочитаемости программы:
В конце концов, люди тоже должны читать программы… Сахарные токены служат в качестве полезных ориентиров, помогающих вам не сбиться с пути…
В качестве примера такого синтаксического сахара приводится «then» в операторе «if» или «do» в операторе «while», а также точка с запятой: компилятор и без них однозначно определяет конец условия и место завершения оператора, но наличие этих конструкций делает программу более удобочитаемой. Очевидно, узкая трактовка понятия «синтаксический сахар» несовместима с широкой: в Си или Паскале невозможно записать операторы иным способом, без «then», «do» и точки с запятой. В таком случае уместно говорить о «синтаксическом мусоре». Учитывая же, что лишние в языке программирования слова — это лишние лексемы, то правильнее было бы применять термин «лексический мусор»[2]. С другой стороны, называть такие «лишние» элементы языка «мусором» не вполне корректно, ведь в действительности они могут существенно влиять на качество программирования, поскольку наличие избыточности в синтаксисе упрощает компилятору локализацию ошибок в коде. Рассмотрим пример на некоем условном Бейсик-подобном языке, где необязательно слово then в условном операторе и точка с запятой между операторами, а знак равенства может обозначать, в зависимости от положения, как логическое равенство, так и присваивание:
if a > b and k = 20 f = 10
Здесь «a>b and k=20» — это условие, а «f=10» — ветвь «то». Однако если программист пропустит или случайно удалит оператор «and», конструкция превратится в:
if a > b k = 20 f = 10
Программа останется синтаксически корректной, но условием будет уже просто «a>b», а ветвью «то», в зависимости от правил языка, либо «k=20», превратившееся из условия в присваивание, либо оба оператора «k=20 f=10». В результате ошибки будет нарушено условие и произойдёт разрушение значения переменной k. Так как при внесении логической ошибки программа останется синтаксически верной, компилятор ошибки не заметит. Требование обязательного наличия служебного слова «then» между условием и оператором приведёт к тому, что компилятор обнаружит синтаксическую ошибку в условии. Обязательность точки с запятой между операторами также позволит компилятору обнаружить ошибку — отсутствие точки с запятой после оператора «k=20». Таким образом, наличие «сахарных» токенов, как и вообще любая избыточность в языке, приводит к тому, что логические ошибки в коде превращаются в синтаксические и могут быть обнаружены компилятором.
Происхождение
Термин «синтаксический сахар» (англ. syntactic sugar) был введён Питером Лэндином (Peter J. Landin) в 1964 году для описания поверхностного синтаксиса простого алголоподобного языка, семантически определяемого в терминах аппликативных выражений лямбда-исчисления с последующей чисто лексической заменой λ на where
.
Примеры
Массивы в Си
Массивы в Си представляют собой блоки в памяти. Доступ к элементам массива производится через указатель на начало блока памяти (то есть, на начало массива) и смещение элемента относительно начального адреса. Это может быть записано без использования специального синтаксиса для массивов (a
— указатель на начало массива, i
— индекс элемента): *(a + i)
, но язык предоставляет специальный синтаксис: a[i]
. Интересно, что можно использовать и форму i[a]
, совершенно логичную вследствие коммутативности операции сложения.
Переопределение операторов
К синтаксическому сахару можно отнести и переопределение операторов, поддерживаемое многими языками программирования. В принципе, любая операция может быть оформлена как процедура (функция, метод). Переопределение операторов позволяет выполнять операции, созданные программистом, внешне так же, как и встроенные в язык[3][4].
Свойства
Ещё одним примером синтаксического сахара является концепция «свойств», поддерживаемая многими современными языками программирования. Имеется в виду объявление в классе псевдополей, которые внешне ведут себя как поля класса (имеют имя, тип, допускают присваивание и чтение), но в действительности таковыми не являются. Каждое обращение к свойству преобразуется компилятором в вызов метода доступа. Свойства совершенно не являются необходимыми (методы доступа можно вызывать и непосредственно) и используются исключительно для удобства, поскольку код с использованием свойств выглядит несколько проще и понятнее.
Критика
Не все программисты считают наличие синтаксического сахара в языках программирования и использование его программистами благом. Известна точка зрения Никлауса Вирта, которую разделяет часть программистского сообщества: согласно ей, любое расширение языка, не вызванное необходимостью, ухудшает его, так как приводит к усложнению транслятора и, соответственно, к понижению его надёжности и производительности. Одновременно возрастает сложность изучения языка и сложность сопровождения программ. Кроме того, сам факт наличия дополнительных синтаксических средств часто играет провоцирующую роль: он побуждает программиста прибегать к различным синтаксическим трюкам вместо того, чтобы глубже анализировать задачу и реализовывать более эффективные алгоритмы. Эти взгляды отразились в языках семейства Оберон, очень простых и практически лишённых синтаксического сахара.
Известен афоризм Алана Перлиса: «Синтаксический сахар вызывает рак точек с запятой». Точка с запятой (;
), являясь обязательной частью большинства популярных языков программирования, даже если в новом языке бесполезна, оставляется как необязательный элемент, так как большинство программистов имеют прочную привычку её использования. В оригинале афоризм обыгрывает созвучие английских слов semicolon («точка с запятой») и colon, последнее из которых означает не только двоеточие, но и толстый кишечник (colon cancer — «рак толстого кишечника»).
Чаще критика направляется на отдельные, часто встречающиеся виды синтаксического сахара: переопределение операций, свойства, сложные операции (вроде тернарной условной операции). Доводы критиков, в основном, сводятся к тому, что подобные средства, в действительности, не делают программу ни проще, ни понятнее, ни эффективнее, ни короче, но приводят к дополнительной трате ресурсов и усложняют восприятие, а значит и сопровождение программы.
Синтаксическая соль
В противоположность «синтаксическому сахару», понятие «синтаксическая соль» (англ. syntactic salt)[5] на жаргоне программистов обозначает технически бесполезные конструкции в языке программирования, которые правила языка требуют употреблять при выполнении потенциально небезопасных действий. Они вводятся в язык только для того, чтобы, используя их, программист тем самым подтверждал, что сомнительное действие предпринято им сознательно, а не является случайной ошибкой или результатом непонимания. Как и «синтаксический сахар», «синтаксическая соль» не расширяет возможности языка и не нужна транслятору для корректной компиляции программы; она предназначена исключительно для людей, пользующихся данным языком. Классическим примером общеизвестной и широко применяемой «синтаксической соли» являются имеющиеся почти в любом языке со статической типизацией встроенные команды преобразования типов данных. Формально эти команды излишни (что демонстрирует классический Си, в котором любое преобразование типов допустимо и выполняется автоматически), но в языках, где их применение обязательно, программист вынужден каждый раз обращать внимание на то, что он выполняет потенциально опасное смешение типов, которое часто указывает на логическую ошибку в программе. В зависимости от строгости языка программирования использование «синтаксической соли» может быть обязательным или факультативным. В первом случае транслятор воспринимает её отсутствие как синтаксическую ошибку, во втором — выдаёт при трансляции предупреждение, которое программист может проигнорировать.
В отличие от «синтаксического сахара», который расширяет свободу выражения программиста, «синтаксическая соль» её сужает, требуя «без причины» писать длинные конструкции. В Jargon File написано: «синтаксическая соль вредна, поскольку повышает артериальное давление хакера». Действительно, при написании небольших программ, создаваемых и поддерживаемых одним человеком, предосторожности могут показаться излишними, однако при промышленной разработке крупных программных комплексов, поддерживаемых большими коллективами программистов, зачастую, к тому же, не самой высокой квалификации, «синтаксическая соль» помогает не ошибаться в разработке и эффективнее разбираться в коде, написанном другими разработчиками.
Примеры:
- Директива
override
в Delphi явно указывает на то, что помеченный ею метод подменяет виртуальный метод класса-родителя. Наличие этой директивы требует от компилятора проверить соответствие сигнатуры подменяющего и подменяемого метода, так что при изменении в базовом классе программист будет вынужден внести те же изменения в классы-потомки, иначе программа не будет компилироваться.
- Примечательно, что в C++ изначально подмена виртуального метода происходила при совпадении сигнатур, но в стандарте «C++11» была добавлена директива
override
, работающая так же, как в Delphi. Её применение необязательно, но рекомендуется из соображений надёжности.
- Операция
reinterpret_cast
в C++ обеспечивает небезопасное преобразование типа. Операция не производит никакого кода, она лишь позволяет программисту обойти контроль типов. Единственный смысл её использования — прямое указание на то, что небезопасное преобразование типов использовано намеренно. - Ключевое слово
unsafe
в Rust в сигнатурах функций и перед блоками кода.
Примечания
- Джек Креншоу, «Давайте создадим компилятор!», раздел «Синтаксический сахар» (недоступная ссылка). Дата обращения: 21 апреля 2013. Архивировано 10 июня 2015 года.
- Синтаксический сахар
- Landin, 1964.
- Abelson, Sussman, Sussman, 1996, Chapter 1, footnote 11.
- syntactic salt
Литература
- Landin, Peter J. The mechanical evaluation of expressions (англ.). — Computer Journal, 1964. — Vol. 6, no. 4. — P. 308–320. — doi:10.1093/comjnl/6.4.308.
- Abelson, Harold, Sussman, Gerald Jay, Sussman, Julie. Structure and Interpretation of Computer Programs. — MIT Press, 1996. — ISBN 0-262-51087-1.
- Benjamin Pierce. Types and Programming Languages. — MIT Press, 2002. — P. 119—121. — 645 p. — ISBN 0-262-16209-1.