Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

Синтаксический сахар

Прежде, чем рассуждать, в каком стиле должен быть выдержан синтаксис идеального языка программирования, давайте почитаем, что об этом говорили до нас. Привожу выжимку из раздела «Синтаксический сахар» из книги Джека Креншоу «Давайте создадим компилятор!».

            Синтаксический сахар — это такие конструкции языка программирования, которые совершенно не нужны компилятору. Синтаксический сахар нужен программисту для чтения и понимания программ. Примеры синтаксического сахара: точка с запятой; «THEN» после «IF»; «DO» перед «WHILE». Эти элементы синтаксиса «утяжеляют» язык и компилятор. Но часто делают программы более понятными.
            Существует две школы в этом вопросе: Си и Паскаль.
            Сторонники Си-шного подхода считают, что синтаксический сахар должен быть выброшен. Язык и компилятор от этого станут компактнее. Программисты будут делать меньше нажатий на клавиши. Каждая дополнительная конструкция — капкан, который ждёт «своего клиента». Лучший способ избежать таких ловушек — исключить их из языка вообще.
            Оппоненты из лагеря Паскаля считают, что набор нескольких дополнительных символов — малая цена за удобочитаемость. Люди тоже имеют право читать программы. Сахарные токены — полезный ориентир, чтобы не сбиться с пути. Ошибочная конструкция на Си часто не является формально ошибочной, она вполне допустима синтаксическими правилами. Их аргумент в том, что каждая такая конструкция является возможностью сообщить компилятору, что вы действительно хотите того, что сказали.

            Лучший пример полезного сахара — точка с запятой. Вот код:
            a=1+(2*b+c) b
компилятор поймёт, что b — начало нового утверждения. На самом деле хотели написать:
            a=1+(2*b+c)*b
Если после и будет точка с запятой, то нет сомнений, где заканчивается утверждение. Здесь синтаксический сахар — дополнительная подстраховка.
            Я нахожусь где-то посередине между этими подходами. Я склоняюсь к преимуществам Паскалевской точки зрения, чтобы находить ошибки во время компиляции, а не во время выполнения. Но я ненавижу просто бросаться словами без явной причины, как в COBOL. Я могу позволить синтаксическому сахару существовать в небольшом количестве.

Не увлекайтесь сахаром.
            Для начала хотелось бы возразить Джеку Креншоу, что точка с запятой тоже может играть деструктивную роль. Например:
	if (a > b);
	    a = 0;
или
	for (i=0; i<100; i++);
	    function();
            В приведённых примерах точка с запятой после «if» и «for» была поставлена по ошибке. Но синтаксис языка допускает это, обрывая в первом случае условное выражение, а во втором — цикл. В результате мы имеем код, в котором точка с запятой «спряталась», и надо приложить усилия, чтобы её обнаружить. В этом случае было бы полезнее иметь в качестве разделителя лексем «перевод строки»: его не заметить невозможно!

             Второе возражение. Цитирую:

«... каждая такая конструкция является возможностью сообщить компилятору, что вы действительно хотите того, что сказали ...

Что на самом деле было сказано компилятору, выясняется только во время всестороннего анализа и тестирования программы. Нередко фразу «я сказал компилятору» после такой проверки следует заменять на «я думал, что я сказал компилятору вот это, а на самом деле сказал другое».

             Теперь вернёмся к теме разговора.

Cинтаксический сахар заменяем графическим

             Программисты XXI века не могут почувствовать на своей шкуре, что такое вводить код программы в память компьютера с помощью тумблеров и кнопок. Или набивать его на перфокарты, нумеруя их карандашом. Без этой нумерации потом трудно найти нужную перфокарту для замены ошибочного кода. До появления дисплеев программисты видели свой код только на бумаге. Поэтому синтаксический сахар был одним из средств улучшения восприятия программы. Но нужен ли он в эпоху GUI ?

             Давно сложившиеся стереотипы говорят нам, что если программист нуждается в синтаксическом сахаре, то он сам своими руками должен долбить этот сахар на клавиатуре. Классики классифицировали эту проблему как «спасение утопающих». Но почему бы эту задачу не переложить на компьютер? Ведь он трудолюбивее программиста.

             Компиляторы и редакторы текста программ давно интегрированы в IDE. Пусть IDE этим и занимается. Нет, IDE не должна самостоятельно вставлять «THEN» между «IF» и «ELSE». Пусть текст программы останется неизменным. Программист должен видеть на экране свою программу, которую IDE в значительной степени преобразила, дополнила графическими элементами.

             При этом разделение труда таково. Программист вводит текст программы. Этот текст максимальным образом лишён всех синтаксических излишеств. А вот IDE по мере ввода программистом программы, анализирует его и дополняет графикой! Для программиста синтаксический сахар возникает сам по себе. Он не прикладывает для этого дополнительных усилий. Каков должен быть сахар — решает сам программист, меняя настройки IDE. Нужно сопровождать элементы конструкций графикой — программист выставляет соответствующие настройки IDE. Для каждого типа графических элементов.

Синтаксический сахар или синтаксический мусор? Или лексический мусор?

Наводим порядок в терминологии. Само понятие «синтаксический сахар» не имеет научного определения, оно сложилось стихийно. Существует две трактовки этого понятия:
  • доминирующая: это возможность записать одинаковые по сути конструкции разными способами;
  • трактовка Креншоу, имеющая место быть при проектировании нового языка программирования, когда некоторые конструкции этого языка вводятся лишь для «наглядности».
Во втором случае синтаксический сахар было бы правильнее назвать синтаксическим мусором. А если учесть, что «лишние слова» в языке программирования — это лишние лексемы, то ещё правильнее назвать это лексическим мусором.

Что ещё почитать на эту тему

Опубликовано: 2012.09.25, последняя правка: 2018.10.29    16:03

ОценитеОценки посетителей
   ███████████████████████ 23 (53.4%)
   ███████████████ 15 (34.8%)
   ██ 2 (4.65%)
   ███ 3 (6.97%)

Отзывы

     2013/04/18 20:13, Михаил          # 

Синтаксический сахар это то, без чего можно обойтись, выразив через другие конструкции языка. От составного оператора отказаться нельзя в языках паскаль или С, кроме как перепроектировав его, но это будет уже другой язык. Собственно Модула и есть сильно перепроектированный Паскаль.

     2013/04/19 13:45, Автор сайта          # 

Почитайте Джека Креншоу, главу «синтаксический сахар». Он этот термин применяет в более широком смысле. Не в смысле «выражение одних и тех же вещей разными способами». Креншоу рассуждает о синтаксисе языка, и «лишние вещи», без которых можно обойтись, он как раз обозначает этим термином.

Возьмём его цитату: «Самым лучшим примером полезного сахара является непосредственно точка с запятой». В узком смысле точка с запятой не является синтаксическим сахаром: без неё невозможно обойтись ни в Си, ни в Паскале, написав как-то по-другому. В широком же понимании этого термина без точки запятой обойтись можно, как обходятся, к примеру, в Ruby или Nemerle

Да, Вы правы, когда говорите о синтаксическом сахаре в уже существующих языках. В процессе же проектирования нового языка «лишние» конструкции, наверное, всё-таки можно назвать «синтаксическим сахаром». А Вы бы как их назвали?

     2013/04/23 14:25, Михаил          # 

Я бы назвал их неудачным решением при проектировании грамматики языка. Всё-таки называть синтаксическим сахаром то, без чего существующая грамматика языка развалится не вполне верно. Например, из Си можно полностью и безболезненно удалить цикл for, семантика языка не пострадает.

     2013/04/25 11:07, Автор сайта          # 

Когда язык программирования делается с чистого листа, то как бы Вы назвали то, без чего в языке можно обойтись? Когда компилятору элемент синтаксиса не нужен, но некоторые программисты находят его улучшающими читаемость? В Википедии пишут: «понятие синтаксического сахара во многом условно». Там же есть ссылка на Креншоу; я употребляю это понятие в том же смысле, что и он.

«Другой же способ записи для уже имеющихся синтаксический конструкций» часто использует другую семантику, поэтому это уместнее назвать «семантическим сахаром». Если цикл заменяется рекурсией — это совсем иная семантика, не правда ли? А синтаксис — этот лишь способ записи определённой семантики, изыски оформления. Присутствие «then» в операторе «if» совсем необязательно, это лишь «украшение». Можно составить грамматику языка так, чтобы без него обойтись. Конечно, если вести речь о уже готовых языках, то обойтись без «then» уже нельзя, если он «зашит» в грамматику, а грамматика — в компилятор.

Но у нас стоит другая задача — придумать новую грамматику. При этом без лишних синтаксических элементов, которые включают в языки «для наглядности», можно обойтись, заменив их на графические. Вот о чём идёт речь.

     2016/08/10 20:23, rst256          # 

Присутствие «then» в операторе «if» совсем необязательно, это лишь «украшение».

Я с вами полностью согласен, так почему же вы с собой не согласны:

2016/06/03 12:14, автор: Автор сайта
Конец строки (точнее — «конец выражения») после условия должен быть обязательно. А конец строки — это либо 16"0d0a", либо точка с запятой. Приведённый пример можно оформить и так:
x = (if x==y; -a; else -b)

Вы же прекрасно понимаете что "then" просто лексический сепаратор, не важно, что вы задействуете в его качестве. Для языков, чей синтаксис не позволяет без сепаратора точно определять конец выражения "then" и ";" необходимы компилятору не меньше чем Си-строке нультерминирующий символ. Я приводил пример языка Lua, где реализовано "мягкое отсечение" для выражений. Вы согласны, что "then" и ";" надо убрать, значит надо убрать:

1. "x = (if x==y; -a; else -b)" — условный оператор внутри выражения должен быть как в си, а для трех и более этажных выражений более подойдет конструкция аналогичная сишной "({ ...; ...; ... })"

2. "-a=...; *a=...;" и все иные унарные операции, совпадающие с бинарными в начале нового предложения, быть не должно. Можно сделать "--" и "++" в этой части постфиксными, ну а разадресация у вас уже и так не конфликтует.

В синтаксисе, который описан на данный момент, других препятствий более нету.

     2016/08/19 03:26, rst256          # 

А как насчет разделителя после последнего элемента списка? Например "," в списке аргументов функции, элементы перечисления и т.п. Есть ли резон принудительно их запрещать?
Это один из самых "невкусных" видов синтаксического сахара, который заставляет и компилятор и программиста делать лишнюю работу.

     2016/08/20 06:27, rst256          # 

Как вам язык ВООБЩЕ с разделителями только когда это необходимо?
void foo(...){...}
...
foo(i2)
foo(i2 55 6+7 bar(...)+2) // => foo(i2, 55, 6+7, bar(...)+2)
foo(i2, -i3 6+7) // => foo(i2, -i3, 6+7)
/* а кому не нравится могут ставить их всегда */
foo(i2, 55, 6+7l, bar(...)+2);
foo(i2, -i3, 6+7);
Так делается в языке Lua где нет разделителей между отдельными командами, однако допустимо их явное указание. На нем код "а1=55+1 а2=а1+3" эквивалентен коду "а1=55+1; а2=а1+3;", и оба этих варианта допустимы в языке.

Правда, разделители между аргументами функций "," там все же есть. Но почему бы не пойти дальше?

     2020/09/28 12:37, kt          # 

Баланс между краткостью, читаемостью и, я бы добавил, понятностью — дело не простое и субъективное. С моей точки зрения синтаксический сахар приятен, когда он необязателен. Примеры из языка, которым пользуюсь:
1. Точку с запятой в конце всего текста можно не ставить, но можно ставить и не вспоминать об этом исключении.
2. Оператор закрытия файла:
CLOSE FILE(F);
но можно:
CLOSE(F);
3. Оператор открытия — тоже можно без FILE, но тогда имя обязательно в начале оператора
OPEN(F) INPUT TITLE(‘T.TXT’);
вместо
OPEN INPUT TITLE('T.TXT') FILE(F);
4. То же самое с размерностью массива при описании без ключевого слова DIMENSION обязательно сначала размерность:
DCL X(-10:10) FLOAT(53);
а с ключевым словом DIMENSION в любом месте оператора:
DCL X FLOAT(53) DIM(-10:10);
5. RETURNS в заголовке процедуры необязателен, но с ним как-то более читабельно:
F:PROC(X,Y) RETURNS(FLOAT);
но можно короче:
F:PROC(X,Y) FLOAT;
В общем, в исходный текст можно самому добавлять сахару по вкусу )) А можно лаконичнее и без сладкого.

     2020/09/28 22:41, Автор сайта          # 

Не знаю, как правильно это называется... Но, допустим, это называется многоместные процедуры, когда аргументы «размазаны» и «приклеены» к нескольким ключевым словам/вызовам подпрограмм. Например:
 СЛОВО_1(Параметр_1) СЛОВО_2(Параметр_2) . . . 
Сдаётся мне, что это наследие Кобола, которое можно встретить не только в PL/1, но и в языке xBase. В современных языках от этого ушли, и вместо
OPEN INPUT TITLE('T.TXT') FILE(F);
или
OPEN(F) INPUT TITLE(‘T.TXT’);
пишут лаконичнее:
F = FOPEN('T.TXT', READ)
Никакого многословия. На мой субъективный вкус так лучше. Объявление массива
DCL X FLOAT(53) DIM(-10:10);
записал бы так:
X = ARRAY(-10:10, FLOAT(53))
а
F:PROC(X,Y) RETURNS(FLOAT);
записал бы
F = FLOAT PROC(X, Y)
Такая запись — в русле следования единообразия, которое обсуждалось в «Слева направо или справа налево?». Если мы присваиваем объекту значение функции, то где находится объект относительно функции/процедуры? Слева? Тогда и в объявлении возвращаемый тип должен стоять слева.

Ещё вспоминается мысль:

Это как код Хаффмана: если в одном месте удалось быть кратким, в другом придётся быть многословным.

Это из недавнего:«О создании языков». Хотя и не всегда обстоит именно так.

     2020/09/29 12:03, kt          # 

Сдаётся мне, что это наследие Кобола, которое можно встретить не только в PL/1, но и в языке xBase.

Да, в Коболе было много сахара, можно было получить диабет)

В современных языках от этого ушли

Да, ушли от крайности Кобола, но приближаются к другой крайности — АПЛ. И шутка про Hello, world! в виде
+++++++++[>+++++>++++++.+++>+<<<<-]>++
.>+.++++..+++.>.<<<<++++++..<><>+.>
становится все менее смешной
Это было хорошо описано ещё в статье на Хабре «Проклятие фигурных скобочек»:
fn main() {
println!("Hello, world!");
}

Что мы видим? fn, фигурные скобки, точку с запятой, println!. Это макрос, как нам поясняет автор. Строковая константа это строковая константа.
Очевидно, разработчики потрудились над сокращенным написанием ключевого слова fn. Это вам не кричащее PROCEDURE, которое писать долго, ещё надо Shift зажимать. Этот пункт однозначное улучшение, по сравнению с восьмидесятыми. Функция main() возвращает ничего. Пустоту. Результат void, даже указывать не надо. Попробуйте объяснить void пятиклассникам. То-то же.
Фигурные операторные скобки. Такие скобки, как известно, экономили память на машинах 70-х. Теперь они экономят время программиста. Ему некогда писать BEGIN END, ведь 21-й век за окном. Время продуктивности.

Далее следует объявление переменной, которое совмещено с инициализацией и размещено прямо в коде. Ну а как еще, не делать же отдельную секцию переменных, как в восьмидесятых. Тогда угрюмые колхозники писали все переменные в строго выделенном месте, чтобы потом их в этом месте найти. Вот так ограничивали свободу выражения творческих личностей. Принуждали к порядку. Хорошо, что мы избавились от этого пережитка.

Стрелочка => которую вы могли принять за составной оператор сравнения с точки зрения арабской письменности на самом деле является указателем на выражение, соответствующее варианту сопоставления.

Экономия усилий программиста во всем, в именовании методов, в синтаксисе замыканий. В отсутствии необходимости указывать тип данных. А что, и так всё понятно.

Если чуть серьезнее, ещё в Лиспе пытались следовать правилу изображения «всё — списки», а сейчас стремятся к правилу «всё — присваивания».
В ПЛ/1 пытались сделать «всё — объекты, начинающиеся с ключевых слов», а как раз присваивание — исключение из этого правила. Поэтому
F = FLOAT PROC(X, Y)
я воспринимаю, скорее, как обращение к функции, чем как заголовок её описания. И отсутствие точки с запятой в конце строки напрягает. Уж очень привык расставлять их по принципу «кашу маслом не испортишь». А концы строк — это мое личное дело и на транслятор влиять не должны.

     2020/09/30 22:44, Автор сайта          # 

Упомянутую статью читал внимательно в своё время. Аргументация автора мне понятна, и я даже сопереживаю автору в его ностальгии. Но это эмоции, а разум стоит на противоположной позиции: «Я понимаю, что раньше «PROCEDURE» звучало, как песнь небесная, а «END» был и твёрже, и толще. Но пойми, время такое: если раньше тебе билет продавали тётки на кассе, то теперь бездушные автоматы. Чуть замешкаешься — заменит тебя автомат. И прощай, Акелла, ты промахнулся. Пока ты набирал «FUNCTION», твой соперник набрал «fn» и занял твоё место на вершине».

Приходится констатировать, что языки становятся всё более IDE-ориентированные. Удаляется синтаксический сахар, но IDE автоматически добавляет графический сахар. Цветовая окраска как символов, так и фона, выделение шрифтами, иными графическими изысками — всё это делает синтаксический сахар анахронизмом. Он не нужен, ибо IDE хорошо расскажет, кто есть ху в исходнике. «PROCEDURE», «BEGIN», «END» — всё это посягает на самый ценный человеческий ресурс — время. Выросло целое поколение разработчиков, которое вкусило плоды цивилизации и несогласно бессмысленно тратить время.

Объявление, совмещённое с инициализацией, устраняет целый класс ошибок — неинициализированные переменные. Вполне благородная цель и не грязные методы достижения.

я воспринимаю, скорее, как обращение к функции

Это потому что без IDE. А IDE подкрасит, как минимум, ключевое слово, и назначение конструкции станет прозрачным.

Утвердился в мнении, что конец выражения должен наступать как с концом строки, так и с точкой с запятой. Их можно ставить в любом количестве и в любом сочетании. Демократия :)

     2021/03/27 02:17, Виталий Монастырский          # 

Ну, как бы, если принять, что мы говорим именно о первой трактовке термина "синтаксический сахар", то его никакая IDE просто не сможет добавить ибо это будет лишено смысла.
Например инкремент существует для того, чтобы заменить набор i=i+i более коротким i++. Какой тогда смысл программисту набирать первое только для того, чтобы IDE подставила вместо него второе, да ещё и в графическом виде? В этом случае сам по себе синтаксический сахар просто лишен смысла.
Я лично считаю, что в языке следует максимально строго следовать принципам однозначности, а потому все и вся должно набираться только одним и единственным способом. Точно так же и наоборот — любой оператор должен трактоваться только одним образом. Однако пару тройку мелочей, вроде того же инкремента/декремента, можно себе позволить. Это не только сокращает набор часто используемых конструкций, но и улучшает видимость определенных операторов. Например в цикле гораздо легче увидеть и убедиться в том, что не забыл поставить инкремент, чем обычную конструкцию присваивания нового значения переменной. А ведь если забыл его поставить, то цикл во время выполнения войдет в мертвую петлю, а это уже потребует закрытия консоли, и нового захода в папку с файлами. И это хорошо ещё если код очевидно покажет, что он в мертвом цикле, а если там идут какие-то сложные расчеты и ты изначально готов к тому, что они будут идти долгое время — ни за что не поймешь: закончила машина считать или это уже мертвый цикл... потому что... а не забыл ли я поставить инкремент в конце цикла!?
Так что предельно малое количество синсаха полезно и даже необходимо в определенных философиях языка.

Добавить свой отзыв

Написать автору можно на электронную почту
mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Содержание

Каким должен быть язык программирования?

Анализ и критика

●  Устарел ли текст как форма представления программы

●  Русский язык и программирование

●  Многоязыковое программирование

Синтаксис языков программирования

Синтаксический сахар

●  Некоторые «вкусности» Алгол-68

●  «Двухмерный» синтаксис Python

●  Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

●  Должна ли программа быть удобочитаемой?

●  Стиль языка программирования

●  Тексто-графическое представление программы

●●  Разделители

●●  Строки программы

●●  Слева направо или справа налево?

●  Комментарии

●●  Длинные комментарии

●●  Короткие комментарии

●●  Комментарии автоматической генерации документации

●●  Нерабочий код

●●  Помеченные комментарии

●  Нужны ли беззнаковые целые?

●  Шестнадцатиричные и двоичные константы

●  Условные операторы

●  Переключатель

●  Циклы

●●  Продолжение цикла и выход из него

●  Некошерный «goto»

●  Изменение приоритетов операций

●  Операции присвоения и проверки на равенство. Возможно ли одинаковое обозначение?

●  Так ли нужны операции «&&», «||» и «^^»?

●  Постфиксные инкремент и декремент

●  Почему в PHP для конкатенации строк используется «.»?

●  Указатели и ссылки в C++

●●  О неправомерном доступе к памяти через указатели

●  Обработка ошибок

●  Функциональное программирование

●●  Нечистые действия в чистых функциях

●●  О чистоте и нечистоте функций и языков

●●  Макросы — это чистые функции, исполняемые во время компиляции

●●  Хаскелл, детище британских учёных

●●  Измеряем замедление при вызове функций высших порядков

●●  C vs Haskell: сравнение скорости на простом примере

●●  Уникальность имён функций: за и против

●●  Каррирование: для чего и как

●●  О тестах, доказывающих отсутствие ошибок

●  Надёжные программы из ненадёжных компонентов

●●  О многократном резервировании функций

●  Использование памяти

●  Почему динамическое распределение памяти — это плохо

●  Как обеспечить возврат функциями объектов переменной длины?

●●  Типы переменного размера (dynamically sized types, DST) в языке Rust

●●  Массивы переменной длины в C/C++

●●  Размещение объектов в стеке, традиционный подход

●●  Размещение объектов переменной длины с использованием множества стеков

●●  Размещение объектов переменной длины с использованием двух стеков

●●  Реализация двухстековой модели размещения данных

●●  Двухстековая модель: тесты на скорость

●●  Изменение длины объекта в стеке во время исполнения

●●  Размещение объектов переменной длины с использованием одного стека

●  Можно ли забыть о «куче», если объекты переменной длины хранить в стеке

●  Безопасность и размещение объектов переменной длины в стеке

●  Массивы, структуры, типы, классы переменной длины

●  О хранении данных в стеке, вместо заключения

●  Реализация параметрического полиморфизма

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

Компьютерный юмор

Новости и прочее




Последние отзывы

2024/04/23 15:57 ••• Ivan
Энтузиасты-разработчики компиляторов и их проекты

2024/04/23 00:00 ••• alextretyak
Признаки устаревшего языка

2024/04/21 00:00 ••• alextretyak
Постфиксные инкремент и декремент

2024/04/20 21:28 ••• Бурановский дедушка
Русский язык и программирование

2024/04/07 15:33 ••• MihalNik
Все языки эквивалентны. Но некоторые из них эквивалентнее других

2024/04/01 23:39 ••• Бурановский дедушка
Новости и прочее

2024/04/01 23:32 ••• Бурановский дедушка
Русской операционной системой должна стать ReactOS

2024/03/22 20:41 ••• void
Раскрутка компилятора

2024/03/20 19:54 ••• kt
О многократном резервировании функций

2024/03/20 13:13 ••• Неслучайный читатель
Надёжные программы из ненадёжных компонентов

2024/03/07 14:16 ••• Неслучайный читатель
«Двухмерный» синтаксис Python

2024/03/03 16:49 ••• Автор сайта
О неправомерном доступе к памяти через указатели

2024/02/28 18:59 ••• Вежливый Лис
Про лебедей, раков и щук