Pull to refresh

Comments 47

Крутой труд, автору спасибо. Даже не программисту будет интересно, особенно если человек занимается разработкой GUI.
Действительно глубоко.
Особенно про фигурные скобки здорово. Из общего соотношения преимуществ/недостатков складывается впечатление почти абсолютной победы Allman (да и прямо сказано: «в большинстве случаев Allman объективно выигрывает у 1TBS»). В реальности, кажется, он не популярнее соперника (кстати, было бы интересно посмотреть статистику).
Наиболее оптимальным вариантом видится комбинация стилей Allman (как основного) и 1TBS (как вспомогательного, используемого в редких случаях) .

Внутри одного проекта, наверное, было бы странно. Тем не менее, сформулированы какие-то правила оптимального выбора.
Внутри одного проекта, наверное, было бы странно.

А почему «странно»? Могу понять, что в чем-то трудно, но все равно решаемо.

Обычно принимаются же какие-то соглашения на уровне проекта по форматированию. Удачные или не очень, но единообразные. Далее моя субъективная оценка. Позитивная сторона этого единообразия — соответствие эргономическому принципу «наименьшего удивления». Мозг читающего код работает в одном режиме, без внезапных переключений. И этот плюс перевешивает плюсы «локально адаптируемых» стандартов.
Решил посмотреть, что на эту тему сказано в классике, поскольку мой собственный ответ показался мне слабо или не очень внятно аргументированным. Открываю цитируемый Вами в начале статьи «Совершенный код» Р. Мартина.
Раздел «Правила форматирования в группах», всего половина страницы.
«У каждого программиста есть свои любимые правила форматирования, но если он работает в группе, то должен руководствоваться групповыми правилами. Группа разработчиков согласует единый стиль форматирования...» Далее рассказ из личного опыта работы в команде.
Ну и в заключение дублируется то, что сказано в начале. «Должен» и «не должен», без обоснований. Классика позиционирует как аксиому.
Кстати, в этой же книге фигурные скобки расставлены в худшем по версии Вашей публикации стиле. Аналогично в «Паттернах» Банды Четырех и новых «Паттернах» (Фримен и др.). Это просто наугад три взятые с полки книги.

Ну, у Мартина я цитирую то, с чем согласен. Проблема в том, что часто те правила, которые приводят «классики», не имеют под собой каких-то ясных объяснений. Я попытался эти правила найти. Цитата, которую вы приводите характерна тем, что в ней автор оперирует такими понятиями как «любимый», то есть он априори говорит лишь о субъективной составляющей, и оставляет в стороне объективные критерии удобочитаемости.


По поводу скобок, если они стоят на своем месте, то есть не вызывают затруднений при чтении и формируют корректную визуальную структуру, то удивления вызывать они не будут. Почему они должны вызывать у вас удивления, если вы готовы к тому, что в ваших правилах есть известные исключения? Расстановка же по жесткому правилу, которое принципиально не способно учесть весь контекст текста, всегда в большей (для 1TBS ) или меньшей (для Allman ) степени будет приводить к неудовлетворительному результату.


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

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

оба относятся больше к философии. До того момента, когда эксперимент по сравнению качества деятельности программиста при работе с кодами в двух стилях подтвердит одно из двух.
Также эксперимент может показать, что это вообще никакой роли не играло:)

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

Придумал пример не про скобки, но про однообразие. Если поднапрячься, можно и со скобками что-то похожее выдать.

if (условие1) действие 1;
действие 2;
действие 3;
if (условие2) действие 4;
if (длинное-предлинное, уходящее за край экрана условие3)
действие 6;
действие 7;

Действие 6 выполняется всегда или только при условие 3==true? А если при беглом просмотре такого кода?

Прошу прощения, вопрос не понял.

Псевдокод такой, Си-подобный. «условие n» — какая-то булева величина, «действие m» — statement, номера m и n для идентификации элементов (надо было слитно писать, типа «действие_6», но уже с пробелами сделал).
Представьте, что Вы читаете этот код.
Первая строка — условие и действие записаны в одну строку. Так делают. Чаще в javascript, мне кажется, но и в C/С++ и других языках допустимо и встречается. С первым if все ясно: действие 1 выполняется, если (условие 1). Действия 2 и 3 выполняются в любом случае.
Второй if — аналогично, выполнится действие 4, если (условие 2).
И доходим до следующего if. Можно подумать, что действие 6 выполнится в любом случае, поскольку в предыдущих случаях код «ниже if» не относился к if.
То есть, я уже воспринимаю код в режиме, когда одна конструкция if умещается целиком в одной строке. Вот и повод удивиться, когда действие 6 не захочет выполняться (с чего бы это? всего-то условие 3 == false).
Кстати, вполне вероятный пример. Встречал рекомендацию именно короткие стейтменты писать в однострочном варианте. Один в один как в этом примере выходит.
С адаптивным правилом по фигурным скобкам возможны вполне себе похожие на эту ситуации.

Псеводкод я понял. Да, тут можно так подумать. Но это потому, что код некорректно отформатирован (не отражает структуру программы, действие 6 должно иметь отступ). И плохо написан в том смысле, что действие 6 надо по-хорошему заключать в кавычки.


Как это относится к правилам расстановки фигурных скобок, я так и не понял.


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

И плохо написан в том смысле, что действие 6 надо по-хорошему заключать в кавычки.

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

Значит, все было не зря)
В кавычках в коде — текстовый литерал получится вместо стейтмента. Уверен, Вы не это имели ввиду, но не пойму, что именно.

Ох и ах. Фигурные скобки, конечно.

По фэншую нужны, конечно.
В данном случае добавление пробелов не только облегчило чтение отдельных строк за счет явного разделения идентификаторов внутри их, но и (вместе с выравниванием) облегчило их сравнение, посредством образования компактных визуальны групп по вертикали. Для того, чтобы понять, что делает этот код, уже не надо читать отдельно каждую строку.

При отделении названия функции пробелом её название начинает восприниматься как переменная в математическм выражении. И скобок закрывающих не хватает.
Скобки поправил. Спасибо.
При отделении названия функции пробелом её название начинает восприниматься как переменная в математическм выражении.

Я бы прокомментировал это так: этап выделения и чтения идентификаторов находится на более низком уровне, по сравнению с этапом определения их роли, то есть разбора синтаксического выражения. Первый этап явно выигрывает от наличия пробелов, и это определяется нашими психо-физическими особенностями восприятия (т.е. не может быть изменено). То, как интерпретируется выражение, это результат научения и выработанной привычки, и, следовательно, может быть изменено. Мы можем привыкнуть легко читать тексты программ, написанные на разных языках программирования, но отсутствие явного разделения между идентификаторами всегда будет требовать больших ментальных усилий.
Сравните:
b = a * a ( a (a, a), a);
b = a * a ( a (a,a), a);
b = a * a( a( a, a ), a );
b = a * a(a(a,a), a);

Не понимаю, что сравнить, но если выбрать из того, что есть, то последний.

Восприятие.
Первая строка с пробелами читается так:
а умножаем на а, потом делаем что-то в скобках.
Пробел в тексте привычно воспринимается как пауза, отделение нового смыслового блока. Поэтому отделение названия функции пробелом от скобки дробит восприятие.

Относительно первой строки. Во-первых, лишний пробел после первой скобки. Он отрывает аргумент от имени функции.


b = a * a (a (a, a), a)

В принципе, я могу увидеть здесь и a * a с хвостом, но с большей легкостью это интерпретируется правильным образом (возможно, потому что я знаю, что это правильно).


Во-вторых, имена короткие. Я бы даже сказал нереально короткие. Размер пробела оказывается большим по сравнению с размерами идентификаторов, и они воспринимаются как далеко отстоящие друг от друга. Да и оператор весит не намного меньше имени. Возможно из-за этого имя легче отрывается от списка аргументов.


Более реальный вариант выглядел бы как-то так:


result = firstOperand * functionName (firstArgument, secondArgument)

Здесь в масштабе имен пробел уже не так весом. При желании, конечно, можно и тут напрячься и проинтерпертировать это как firstOperation * functionName, но мой мозг сопротивляется ("Там же список аргументов!")

Все то же. Из-за масштаба просто пробелы выглядят большими. Но список аргументов все равно попадает в поле зрения и учитывается при интерпретации выражения.


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


result = max * max(min, max)

Но тем не менее при первом взгляде на это выражени у меня лично все равно возникает некоторый дискомфорт из-за слипшихся max(min, которые, если смотреть куда-нибудь в строну result или первого max, воспринимаются как что-то единое, с каким-то возмущением посередине.

Речь была о том, что в выражениях переменные будут носить имена подходящие под смысл выражения, а вовсе не operand или functionName.
result = min_current * duty_cycle (vin_min)

Как вариант: префиксы еще добавлять.

Ну, получается, что я вас не понял. И сейчас не совсем понимаю. :)
Потому что, как мне кажется, в контексте того, о чем мы говорим (о расстановке пробелов), конкретные имена не важны. Длина важна.

Так выше как раз пример со средней длины названиями, глядя на которые и расстановку пробелов не сразу понимаешь, где название функции. А если у нее название ещё длинее, а аргументы в скобках короткие или короткий и длинный?

Вы про имена с подчеркиваниями?

Можно и в другом стилем написать, без подчеркиваний, с большими-маленькими.
result = firstLongLongArgument * longLongFunctionName (local, local)

Я не вижу здесь какой-то особенной проблемы с интерпретацией этого выражения.


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


Если так, то я готов с вами согласиться. Наверно, действительно, существуют такие случаи. Те примеры, которые вы приводили, однако, на мой взгляд, к ним не относятся.


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


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

Не упомянут и почему-то редко используется вариант, когда пробелами выделяется список аргументов:


result = someFunction( firstArgument, secondArgument );

Здесь и скобка остаётся у имени функции, и разделены идентификаторы, и акцентировано завершение списка аргументов.
Или ещё из примера со вложенными скобками, где, кстати, завершающая была потеряна:


width = CFStringGetDoubleValue( (CFStringRef)CFArrayGetValueAtIndex(arrayRef,0) );

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


whole( yards() );
while (nine())
Есть в этом что-то. Хотя действительно редко встречается. И выглядит непривычно соответственно. Рука тянется вернуть правильное безобразие.
Хороший вариант. Только не соответствует литературным привычкам.
Я бы его и выбрал. Кстати, раньше так и писал.

Наверно, такой вариант тоже возможен. Его основной недостаток, на мой взгляд, что он не соответсвует привычкам из естественного языка. Кроме того, на мой взгляд, все-таки скобки ассоциируются со списком аргументов. То есть у нас есть имя функции и список аргументов, ограниченный скобками. Пробел в конце выглядит ненужным, его основная функция придать симметрию выражению в скобках.
В общем, неестественно.
Ну и последний аргумент мне тоже кажется надуманным.

Вот ещё момент.


result = someFunction (firstArgument, secondArgument);

Здесь пробел разделяет имя функции и список аргументов, то есть подчеркивает структуру этого выражения, то, что функции передается список аргументов. То есть у нас есть два объекта: функция, представляемая её именем, и жестко (за счет прилегающих скобок) связанный список аргументов.


result = someFunction( firstArgument, secondArgument );

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

Возможны разные случаи сочетания имени функции и списка аргументов. В примерах рассматривается C++ — тогда это объявление, определение и вызов. Для объявления и определения отделение имени функции от скобки выглядит, пожалуй, оправданно:


private void someFunction (Type1 firstArgument, Type2 secondArgument);
...
void SomeClass::someFunction (Type1 firstArgument, Type2 secondArgument)
{

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


width1 = CFStringGet((CFStringRef)first(arrayRef,0), second, third(1));
width2 = CFStringGet ((CFStringRef)first(arrayRef,0), second, third(2));
width3 = CFStringGet( (CFStringRef)first(arrayRef,0), second, third(3) );

Разумеется, отличия в синтаксисе других языков будут влиять на рекомендации, направленные на улучшение зрительного восприятия.
Где-то (например, в Go) не требуются скобки в управляющих конструкциях. В Perl скобки не обязательны даже при вызове функций; это и к вопросу о списках и отдельных аргументах. Вместо круглых скобок может использоваться вертикальная черта (Rust). Во многих языках активно используются угловые скобки, часто вложенные.
И далеко не всегда общепринятый для какого-либо языка стиль написания кода соответствует оптимальному визуальному представлению. OTBS и camelCase побеждают.

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

Моя интерпретация этой синтаксической конструкции отличается от вашей: для меня это вызов функции, который состоит из имени функции и списка аргументов. Аргументы не принадлежат функции, а передаются ей.

Да, наверно именно так и надо писать. Ссылаясь на исследования зрения. Иначе народ не понимает. Описаны те самые вещи которые пытался многим объяснять. Без исследований. Видно же с первого взгляда что так читать тяжело, а так легко. Ни один, никогда не согласился. Люди не видят что они видят. «А мне так удобно». Это как езда на велосипеде с полусогнутыми ногами — «удобно».

К сожалению, даже с результатами исследований, в большинстве случаев результат будет тот же.

Обсуждение смещается в область педагогики)
В Go дизайнеры языка просто запретили расставлять скобки разными способами, принесли удобочитаемость в жертву стандартизации.
image
Мне кажется, здесь вы так сконцетрировали внимание на идентификаторах, игнорируя исключительную важность скобок, что часть скобок растеряли. Я тоже не сразу заметил ошибку, строка воспринимается как набор идентификаторов, где-то там между ними затерялись скобки, что к чему относится, где имена функций, где аргуметы, непонятно.
Я сейчас используя такой стиль:
width = CFStringGetDoubleValue( (CFStringRef) CFArrayGetValueAtIndex( arrayRef, 0 ) )

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

По поводу вашего стиля я уже отписывался выше: тут и тут.


В вашем варианте мне нравится то, что есть пробелы. В остальном нет. Почему, вы можете прочитать выше.


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


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


Ошибка со скобкой достаточно показательна. Во-первых, это хороший пример того, что надо использовать правильный инструмент для редактирования, который делает такие ошибки заметными. Чего я не делал по определенным причинам. Но это относится к редактированию, а не к чтению, и в данной статье не рассматривается. Во-вторых, интересно, что мне отсутствие скобки не мешало воспринимать этот фрагмент правильным образом. Но у меня были определенные ожидания. Сильно ли она помешала вам?

Мне отсутствие скобки несильно помешало, потому что мне и так не сразу понятно было, какие скобки куда относятся. Для меня, как видите, ваш вариант труднее воспринимать. Но восприятие текста гораздо менее важно, чем отсутствие ошибок. Первая задача стиля, как я это вижу, — способствовать минимизации ошибок, а уже потом можно думать над читабельностью.
P.S. Такую расстановку скобок я (и мои коллеги вслед за мной) использую около десяти лет, на работе. Я всегда в поиске более лучшего стиля, и время от времени что-то меняю, но к этим скобкам вопросов еще не возникало.

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


Но вот ваше утверждение:


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

— я принять не могу. Поскольку оно противоречит само себе.


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

А в целом мне ваша статья понравилось, потому что я давно недоумеваю, почему никто не озадачивается таким немаловажным вопросом, как дизайн исходного кода. Как правильно писать текст на веб-страницах уже давно изучают в университетах, а исходный код до сих пор пишут кто на что горазд.
Sign up to leave a comment.