Это удивительные времена, чтобы быть разработчиком программного обеспечения. У нас есть доступ к обширной и многогранной экосистеме очень хорошо продуманных языков программирования, созданных мастерами. Регулярно добавляются новые, и они, как правило, бесплатны для использования и адаптации.
Как человек, который каждый день просматривает большое количество исходного кода, нуждаясь в быстрой оценке его сути, архитектуры и структуры, я понял, что когда дело доходит до синтаксиса, чем меньше, тем лучше. На мой взгляд, это особенно верно для элементов инфраструктуры, таких как скобки, фигурные скобки или точки с запятой. Этот пост в блоге является попыткой объективно сравнить их затраты и выгоды.
Прежде чем мы углубимся в это, давайте напомним себе, что причина № 1 для использования языка программирования — это удовольствие от его использования и выполнения задач. Доставка вещей. Большинство языков достигают этого, действительно потрясающее программное обеспечение было написано во многих разных стеках, и это очень хорошо. С учетом сказанного, если вы немного увлекаетесь синтаксисом языка программирования и не хотите просто придерживаться того, к чему привыкли вы и другие, читайте дальше!
Предварительно приведем несколько простых правил, с которыми, я думаю, мы все можем согласиться, и которые мы будем использовать для оценки различных подходов:
- чем проще, тем лучше: если есть две альтернативы, обеспечивающие более или менее одинаковую функциональность, и одна из них проще, легче или меньше, то эта альтернатива является предпочтительной.
- надежность:выигрывает альтернатива, которая дает людям меньше веревки, на которой они могут повеситься, т. е. меньше возможностей все испортить.
- отношение сигнал/шум кода должно быть высоким. Это означает, что большая часть исходного кода должна описывать бизнес-функциональность. Шаблон (код, предназначенный только для того, чтобы машина работала) должен быть минимальным. Иными словами, код должен выражать намерение разработчика как можно более прямо, без чрезмерной инфраструктуры вокруг него.
Имея это в виду, давайте рассмотрим несколько общих элементов синтаксиса и посмотрим, сможем ли мы внести некоторую ясность в споры вокруг них. Может быть, мы даже сможем придумать, как выглядит идеальный синтаксис языка программирования — если таковой существует. А может это все отмывка. Для простоты примеры сосредоточены на небольших и простых языках, таких как JavaScript/CoffeeScript, Go, Python, Java и т. д. Давайте углубимся!
фигурные скобки
Фигурные скобки заменили еще более старые операторы begin
и end
в Алголе, Паскале и им подобных. Это был большой шаг вперед, так как он экономит много времени на наборе текста, не теряя при этом выразительности. Вопрос в том, являются ли фигурные скобки окончательным выражением разделителей блоков, или мы можем оптимизировать вещи еще больше? Давайте посмотрим на некоторый код. Что делает этот фрагмент:
function max(a,b){log(a,b);if(a>=b){return a}else{return b};
Вы заметили опечатку? Тяжело читать, да? Это потому, что этот код полагается исключительно на разделители, такие как фигурные скобки, круглые скобки и точки с запятой, для описания структуры кода. Давайте отформатируем это более удобно для человека, написав каждое выражение в отдельной строке и добавив отступ:
function max(a, b) {
log(a, b);
if (a >= b) {
return a
} else {
return b
};
Так читабельнее! И гораздо проще увидеть, что в конце отсутствует закрывающая фигурная скобка. Этот эксперимент демонстрирует, что люди используют отступы в качестве основногомеханизма для определения структуры кода, а фигурные скобки используют только в качестве резервного в крайних случаях, например, когда отступы перепутаны и Не очевидно.
Фигурные скобки также вводят дополнительные категории ошибок. Если вы забудете закрыть фигурную скобку, программа будет вести себя непредвиденным образом, даже если фактические операторы в ней (код, который вычисляет аргументы в результаты) верны. Правильный отступ кода, но неправильное размещение фигурных скобок приводит к коду, который делает что-то другое, чем ожидало бы большинство программистов, если только бегло взглянуть на него.
Код с фигурными скобками по-прежнему должен иметь правильный отступ. Когда мы это делаем, этот код использует два разных способа выражения структуры кода: отступы и фигурные скобки. Люди в основном смотрят на отступы и «отфильтровывают» фигурные скобки. Парсеры смотрят только на фигурные скобки и игнорируют отступы. Обе заинтересованные стороны всегда должны на 100% соглашаться с тем, что делает код, а это означает, что эти два способа выражения структуры кода всегда должны идеально синхронизироваться друг с другом. Например, этот код здесь компилируется и работает нормально, но он вводит в заблуждение и его трудно поддерживать, поскольку он неправильно отформатирован, т. е. неправильный отступ:
function max(a, b) {
if (a >= b) {
if (a > 0) {
return a
} else {
return b
} else {
return a
}
}
Итак, если отступы являются наиболее важным аспектом для людей, люди большую часть времени отфильтровывают фигурные скобки, и каждый раз, когда отступы отличаются от структуры кода, мы получаем вводящий в заблуждение код, что произойдет, если мы избавимся от фигурных скобок вообще и только используется отступ?
function max(a, b)
log(a, b)
if (a >= b)
return a
else
return b
Это по-прежнему читаемый и однозначно структурированный код, понятный как людям, так и парсерам. Он также более компактен по горизонтали и вертикали, т.е. использует меньше строк. Это более понятно и позволяет избежать ряда проблем, таких как забытые открывающие или закрывающие фигурные скобки, или должна ли открывающая фигурная скобка быть на отдельной строке или нет. Поскольку ошибки отступов являются ошибками синтаксического анализа для языков, чувствительных к пробелам, мы можем быть уверены, что отступы всегда правильные, и нам не нужны средства форматирования или линтеры, чтобы исправить их за нас, которые люди могут не запускать постоянно.
В лучшем случае, при правильном использовании фигурные скобки просто присутствуют, не добавляют особой читабельности и отфильтровываются людьми, поскольку читабельность в основном обеспечивается отступами. В худшем случае, когда фигурные скобки не соответствуют отступам, разработчики-люди могут быть серьезно введены в заблуждение. В то время как правильные отступы необходимы, как мы видели выше, фигурные скобки избыточны и не нужны. Вот почему мы называем их линейным шумом. Это просто больше материала для ввода, больше материала для проверки, и они существуют в основном для удовлетворения наших привычек на данном этапе. Они являются наследием, и в соответствии с нашими правилами, изложенными выше, нам лучше просто оставить их в стороне.
Точка с запятой для завершения строки
В чем разница между этими двумя кодовыми блоками?
function max(a, b)
log(a, b);
if (a >= b)
return a;
else
return b;
и
function max(a, b)
log(a, b)
if (a >= b)
return a
else
return b
Ничего такого. Никому не нужны точки с запятой в конце строк. Никто не скучает по ним, если их нет. Их единственное использование — разделение нескольких выражений в одной строке. Что в любом случае является анти-шаблоном, так что больше никаких точек с запятой!
Скобки вокруг выражений
Далее, как насчет скобок вокруг выражений? Давайте удалим их из блока кода нашего примера и посмотрим, что произойдет:
function max a, b
log a
if a >= b
return a
else
return b
По-прежнему ясно, что a
и b
являются параметрами функции max
, что мы записываем a
в следующей строке, а затем проверяем, больше ли a
или равно b
.
Подобно фигурным скобкам, круглые скобки — это избыточный способ выражения структуры кода и намерений разработчика. Реальный мир не всегда так прост, как в этом примере, поэтому в более сложном коде определенно можно использовать круглые скобки для группировки подвыражений. Но, похоже, нет необходимости навязывать их для простых случаев. Давайте сделаем их необязательными, чтобы мы могли упростить наш код, где это возможно, не отказываясь от возможности однозначно структурировать более сложные выражения.
Комментарии
Наиболее широко используемые способы добавления комментариев к исходному коду — это //
, /* */
и #
. Давайте сначала посмотрим на комментарии в стиле C:
// a single-line comment
/* a * multi-line * comment */
/** * a JavaDoc comment */
Теперь давайте посмотрим на комментарии через один символ:
# a single-line comment
# a # multi-line # comment
## # a JavaDoc comment
Оба фрагмента кода делают то же самое и одинаково хорошо читаются. Первая версия использует 19 символов комментария и требует отступа последующих строк в многострочных комментариях через один пробел. Во второй версии используется только 7 символов комментария без отступов, что приводит к меньшему количеству строк кода.
По нашим правилам побеждает второй вариант.
Пробелы против табов
Аргументы в пользу использования табуляции для отступа кода:
- Потому что это делает файл исходного кода меньше: 1 табуляция вместо 2 или 4 пробелов.
- Это позволяет избежать дискуссий о том, какой отступ должен быть в коде, позволяя разным разработчикам делать отступы в своем коде в разной степени, настраивая размер вкладки в своих текстовых редакторах.
Первый аргумент исходит из ограничений компьютерных систем 60-летней давности и больше не действует. Второй вид есть, но, как мы увидим, с сильными ограничениями.
Аргументы против вкладок:
- Размер табуляции по умолчанию (8 пробелов) явно слишком велик. Это означает, что КАЖДЫЙ ЧЕЛОВЕК в мире, который просматривает исходный код, теперь должен настроить КАЖДЫЙ ИНСТРУМЕНТ и КАЖДЫЙ ВЕБ-САЙТ, который они используют для написания или просмотра кода, на свой предпочтительный размер вкладки. На КАЖДОЙ МАШИНЕ, которую они используют.
- Есть инструменты, которые не позволяют настроить размер вкладки. Примерами могут служить многие веб-сайты со встроенными фрагментами кода или множество низкоуровневых команд терминала. Эти инструменты часто используются для отображения исходного кода, например, при проверке кода.
- Символ табуляции трудно вставить в поля ввода. Например, в формах поиска и замены клавиша TAB часто выполняет переход к следующему полю ввода. Людям часто приходится копировать и вставлять символ табуляции из своего исходного кода в такие поля, чтобы найти его.
- Не на всех клавиатурах есть клавиша TAB. Например, на большинстве клавиатур на мобильных устройствах его нет. Мобильные устройства играют все большую роль в нашей жизни, в том числе и в разработке программного обеспечения. Я просматриваю большое количество кода на своем мобильном устройстве, и рецензентам иногда нужно предоставить фрагменты кода в качестве примеров.
- Символ табуляции не был придуман для создания отступа в исходном коде. Он существует, чтобы упростить форматирование числового содержимого в столбцы и таблицы с помощью позиций табуляции. В исходном коде нет столбцов, таблиц или позиций табуляции.
- Форматирование с помощью табуляции не поддерживает многие способы форматирования кода в удобочитаемом виде.
Давайте рассмотрим несколько примеров, связанных с последним пунктом. Одним из них является форматирование вызовов функций с большими списками сложных параметров:
draw_circle( canvas_width / 1024 * 10,
canvas_height / 786 * 15,
canvas_width / 1024 * 5,
display.colors.primary,
round(canvas_width / 1024 * 3.5) )
Этот код рисует круг и вычисляет параметры для него в строке. Поскольку аргументы такие сложные, мы хотим поместить каждый из них в отдельную строку, чтобы разделить их визуально. Размещение их за именем функции делает визуально ясным, что они являются параметрами этой функции. Эквивалентный код с использованием вкладок должен переместить все аргументы на следующую строку:
draw_circle(
canvas_width / 1024 * 10,
canvas_height / 786 * 15,
canvas_width / 1024 * 5,
display.colors.primary,
round(canvas_width / 1024 * 3.5))
Проблема с таким форматированием заключается в том, что аргументы draw_circle
теперь слишком похожи на блок кода с отступом. Это сбивает с толку, особенно если прямо под ним будет блок кода с отступом.
Другой — довольно распространенный — случай использования, когда форматирование на основе вкладок не работает, — это цепочка методов: с пробелами код можно отформатировать красиво и ясно:
fancy_box = new Square().fill('red')
.move(-10, 20)
.rotate(35)
.zoom(2.8)
Это дает понять, что мы делаем кучу вещей с экземпляром Square
. С вкладками нам снова приходится перемещать цепочку вызовов на следующую строку, что делает ее слишком похожей на вложенный блок кода:
fancy_box = new Square()
.fill('red')
.move(-10, 20)
.rotate(35)
.zoom(2.8)
Основываясь на этих примерах, мы можем сделать вывод, что использование табуляции для отступа исходного кода может звучать хорошо в теории, но работает только для простого кода в средах, где используется лишь несколько настраиваемых инструментов.
Давайте также оценим плюсы и минусы использования пробелов. Плюсы:
- исходный код выглядит одинаково во всех инструментах и средах
- большая гибкость в элегантном выражении сложных структур кода
- работает со всеми устройствами и клавиатурами
Минусы:
- открывает дебаты о том, сколько пробелов использовать для отступа кода
- открывает дебаты о том, как выглядит «элегантное» форматирование кода
- код форматирования с вкладками кажется более семантическим по своей природе, а использование пробелов кажется более презентационным
Чтобы закончить, давайте поговорим о том, сколько пробелов следует использовать для отступа кода. Наиболее распространенными кандидатами являются 2 и 4, поскольку мы можем (надеюсь) согласиться, что 8 слишком велико, а 1 недостаточно. Учитывая, что многим сообществам нравится ограничивать ширину кода примерно 80 или 100 символами, 2 пробела кажутся предпочтительными, поскольку они оставляют больше горизонтального пространства для кода. Итак, вопрос в том, достаточно ли 2 пробелов для отступа? Давайте посмотрим на наш первоначальный пример, чтобы узнать:
function max a, b
log a, b
if a >= b
return a
else
return b
vs
function max a, b
log a, b
if a >= b
return a
else
return b
Оба стиля работают, версия с двумя пробелами использует меньше горизонтального пространства, поэтому она выигрывает.
Последние мысли
Следование нашим правилам привело нас к пути к минимальному синтаксису, то есть к синтаксису, который сводит к минимуму линейный шум и пытается уйти с дороги, чтобы бизнес-логика и замысел разработчика проявлялись лучше. Это то, с чем большинство из нас сталкивается каждый день, но часто не тратит много времени на размышления. Многие современные языки (то есть те, которые были разработаны в последние два десятилетия) принимают многие из этих элементов в той или иной форме, и, безусловно, в будущем их станет еще больше.
Надеюсь, это было приятное чтение и некоторая пища для размышлений, когда вы решите, какой язык изучать дальше, или разработаете свой собственный язык или формат данных. Независимо от того, какой язык вы в конечном итоге используете, удачного кодирования! :)