Написать пост

Синтаксический сахар. Опаснее, чем кажется?

Аватарка пользователя Ирина Тюльпакова

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

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

Синтаксический сахар делает «человечный» код

Впервые термин «синтаксическое приукрашивание» (или syntatic sugaring) употребил Питер Ландин в «Механической оценке выражений» в 1964. Работа обосновывала введение абстракции и внедрение псевдонимов для лучшей считываемости кода.

Ландин предположил, что если заменить математический знак на слово, это не скажется на смысле, зато поможет человеку лучше понимать код. В качестве примера он использовал псевдоним «where» для обозначения λ-символа. И продемонстрировал, что такая замена пусть и «оставляет некоторые детали неясными, но делает использование where в дальнейшем (а) понятным и (б) правдоподобным простым “синтаксическим приукрашиванием”».

Уже на тот момент у синтаксического сахара было достаточно противников. Например, Никлаус Вирт яростно боролся с «приукрашиванием». Свою позицию на бумаге он выразил в работе «Хорошие идеи: взгляд из Зазеркалья» 2006 года.

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

И несмотря ни на что, синтаксический сахар прижился

Сначала «приукрасили» лексемы, позже внедрили укороченные формы записи типичных операций. Например, тернарный условный оператор. В нём условия true и false разделены двоеточием, не нужно перескакивать через строки, чтобы сравнить или поправить их значения:

			//стандартная запись на С#
if (y < 5)
    {
        x = 5;
    }
else x = y;

//запись с использованием синтаксического сахара
x = ? (y < 5) 5 : y;
		

Скорее всего, вы уже используете синтаксический сахар, просто не замечаете этого. Например, оператор switch

			Switch(i) 
{ 
  case i==0:  
    Console.WriteLine(0); 
    break;
    
  case i==1: 
    Console.WriteLine(1); 
    break;
    
  default:
    Console.WriteLine(“i is incorrect”); 
    break;
 }
		

По сути он является более удобной записью для нескольких ifelse:

			If(i == 0)
{      
  Console.WriteLine(“0”);
} 
else if(i == 1) 
{      
  Console.WriteLine(“1”); 
} 
else 
{      
  Console.WriteLine(“i is incorrect”);
}
		

Цикл for — лишь более удобная запись цикла while:

			//сахар
For(int i = 0; i < 10; i++) 
{ 
  Console.WriteLine(i);
} 

//полная запись
int i = 0;
While(i < 10) 
{ 
  Console.WriteLine(i); 
  i++;
}
		

И так далее.

Но синтаксический сахар не всегда удачное решение

Увлекаться и использовать его постоянно не стоит по нескольким причинам.

Все нетривиальные абстракции в той или иной степени являются «дырявыми»

Это означает, что, применяя абстракцию, разработчик слепо доверяет ей, и у него нет возможности проверить, вызывает ли она дефекты (закон «дырявых абстракций»). В случае с «дырявой» абстракцией в коде компилятор даже не сможет подсказать, что была допущена ошибка. Он просто выполнит формально корректные инструкции.

Взглянем на операторы ++ (инкремент) и += (сложение с присваиванием). Вводные данные: функция NewYear() вызывается для того, чтобы обновить переменную current_year. Известно, что current_year — натуральное число, и в любых языках с ней могут работать обе операции.

			//запись на C#  
public void NewYear(int current_year) 
{
  current_year += 1;
  return current_year;
}
		

Начинающий программист, взглянув на код, подумает: зачем +=, если есть ++. И перепишет:

			public void NewYear(int current_year) 
{
  return current_year++;
}
		

И если переменная current_year глобальная, то в программном коде от такой записи возникнет трудноотслеживаемая ошибка: значение переменной меняется с запозданием. ++ и += реализуют разные возможности языка: оператор, перемещённый в постпозицию, при инкриминировании возвращает значение 2023, и только потом увеличивает его на 1. Операция += присваивает значение 2024 сразу же.

Да, можно записать ++current_year и инкрементация возникнет перед инициализацией переменной. Проблем не будет, если знать, как работает префиксный и постфиксный инкремент. То есть углубиться внутрь абстракции.

Совместная разработка давно обзавелась своими «правилами приличия»

Уважайте время напарников, которым нужно будет разобраться в логике вашего кода.

Как правило, эта беда преследует новичков. На Stack Exchange обсуждали, что напарникам сложно читать казалось бы сахарную конструкцию. Опытные пользователи возразили: «Эти функции языка может быть трудно понять новичкам поначалу, но на самом деле сахар добавили в языки, чтобы сделать код короче», — и порекомендовали привыкнуть.

Так использовать или нет?

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

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

А компьютеру без разницы, в полной или укороченной форме вы запишете токен. Он переведёт знакомые лексемы в байт-код.

Следите за новыми постами
Следите за новыми постами по любимым темам
667 открытий2К показов