Вот в этот: Наименования переменных.
Впечатления, как это обычно бывает после общения с евангелистами-функциональщиками, неважные. Как будто общаешься с подвыпившими людьми – думая о чем-то своем они высказывают суждения, которые, по их мнению, должны быть очевидны всем и каждому. Что совсем не так и для того, чтобы понять контекст высказывания нужно приложить немало усилий.
Когда я слышу разговоры о том, что идентификаторы (имена констант или переменных) должны быть короткими, я инстинктивно считаю, что люди предлагают вместо:
const size_t expected_bytes = calculate_expected_message_size(); const size_t bytes_read = load_data_from_stream( expected_bytes ); if( bytes_read != expected_bytes ) ...; binary_buffer_ptr_t whole_message = extract_raw_message(); binary_buffer_ptr_t message_payload = check_and_remove_protocol_headers( whole_message ); |
писать так:
const size_t e = calculate_expected_message_size(); const size_t r = load_data_from_stream( e ); if( r != e ) ...; binary_buffer_ptr_t m = extract_raw_message(); binary_buffer_ptr_t p = check_and_remove_protocol_headers( m ); |
Чего лично я не приемлю и за что “бью по рукам”. Ничего не поделать, учился у хороших учителей, давно, когда в образовании использовался Паскаль и книжки Вирта. А место, даже на дискетах, под исходные тексты экономить уже было не принято. Кстати, мониторы тогда были еще алфавитно-цифровые, с разрешением 80x25 знаков, так что текста на экране было намного меньше, чем сейчас. И IDE с автодополнением, подсказками и нафигацией по коду не было. Зато хороший стиль кода очень ценился.
Но это я отвлекся. Полагаю, что автор упомянутой заметки имел в виду другой случай. Определяющийся заморочками функциональных языков, в частности иммутабельностью сущностей. Поскольку от функциональщины, к счастью, далек, то попробую проиллюстрировать свою мысль более привычным рядовому программисту примером.
Допустим, нам нужно сформировать SQL-ный select, где количество столбцов определяется внешними параметрами. Как это может выглядеть в императивном стиле с переменными? Как-то вот так:
std::string select_statement = "select id"; if( need_creation_time ) select_statement += ", ctime"; if( need_modificaton_time ) select_statement += ", mtime"; ... select_statement += " from my_table where ..."; |
Переменная одна, название ей дано длинное. А вот если мы напишем что-то подобное в функциональном стиле с иммутабельными сущностями, то получится что-то вроде:
s1 = "select id"; s2 = if( need_creation_time ) s1 + ", ctime" else s1; s3 = if( need_modificaton_time ) s2 + ", mtime" else s2; ... select_statement = sN + " from my_table where ..."; |
Т.е. если давать сущностям s1…sN какие-то вменяемые названия (вроде statement_with_opt_ctime, statement_with_opt_mtime и т.д.), то элементарно задолбешься выдумывать промежуточные названия. Да и смысла большого они нести не будут. Хотя, по моему мнению, обилие сущностей вида s1, s2 и т.д., не есть хорошо. Страшно далеко ФП от народа железа ;)
В общем, в программировании есть две неразрешимые проблемы – предсказание сроков и выбор названий для идентификаторов. Только временами кому-то начинает казаться, что они нашли серебряную пулю решение какой-то из них. Забывая четко обозначить тот сильно ограниченный контекст, в котором их частные решения работают.
PS. Почему я так резко прореагировал на эту тему? Потому, что на днях случайно была найдена серьезная ошибка. В одной из версий программы один разработчик заменил код:
stream << range.m_left << range.m_right << range.m_client << range.m_priority; |
на код:
// Если диапазон оказался слишком большим, то нужно его сузить // принудительно передвинув правую границу. id_t right = range.m_right; if( range.m_right > range.m_left + package_size ) right = range.m_left + package_size; stream << range.m_left << right << range.m_client << range.m_priority; |
Второй разработчик должен был эти же изменения перенести (смержить средствами системы контроля версий) в параллельно развиваемую ветку программы. Полагаю, опытные разработчики уже догадались, что у него получилось:
// Если диапазон оказался слишком большим, то нужно его сузить // принудительно передвинув правую границу. id_t right = range.m_right; if( range.m_right > range.m_left + package_size ) right = range.m_left + package_size; stream << range.m_left << range.m_right << range.m_client << range.m_priority; |
Понятное дело, что ошибка обнаружилась далеко не сразу и случайно. А уж если бы вместо range, right, m_left, m_right использовались бы имена вида R, L, R1, R2 и т.д., то я бы только функциональщикам и пожелал бы сопровождать такой код.
PPS. Еще ссылки:
- заметка в блоге lionet на какое-то исследование понятности написанного в разном стиле кода;
- очень интересная реакция на это исследование в блоге sleepy_drago;
- скептическая точка зрения Бертранда Мейера на околопрограммистские исследования (на английском).