(https://hardcode.fm/2018/07/28/episode001.html 00:40:57)
Часть подкаста про имена и смыслы - охуенная! Не знаю, куда написать фидбэк, поэтому пишу сюда.
Я тоже давно думал про эту дихотомию между многозначными (но знакомыми) и однозначными (но незнакомыми) именами. Мне кажется, это два разных стиля мышления: условно "математический" и условно "поэтический".
Когда мы создаем какое-то математическое построение, имена играют чисто утилитарную роль: по сути, это просто ссылки на ранее определенные структуры, эдакие сишные указатели, но для мозга. Для нас нет разницы, назвать структуру "группой", "театром" или, например, 0xdeadbeef - на самом деле мы создаем новое значение, которого до этого у нас в языке вообще не было, и нам просто нужно как-то к нему обращаться, так что подойдет любое слово, любая последовательность звуков и букв.
Если же мы создаем поэзию, то все наоборот: нам обычно важны сами слова, а не их семантика ("семантика" в смысле некоего точного, отдельного значения, однозначно соотнесенного с этим словом). Какого-то конкретного смысла у слова в данном контексте может вообще не быть - мы играем на связях этого слова с другими словами, аффектами, состояниями, и таким вот опосредованным путем пытаемся передать то самое "облачко", о котором шла речь в эпизоде.
Если есть между математиками и поэтами кое-что общее, то это вот что: и те, и другие не боятся создавать новые слова\смысловые структуры под конкретную задачу, которая перед ними сейчас стоит. Разница в том, что математик рассчитывает на понимание написанного без контекста (на практике - с минимальным контекстом): каждое использованное понятие явным образом вводится (и в пределе весь текст может быть вообще забит в прувер и подвергнут механической обработке), - в то время как поэт (музыкант, кстати, тоже), напротив, рассчитывает на то, что читатель будет иметь общий с ним контекст (языковой, культурный - ну то есть буквально, что читатель читал\слушал то же и испытывал те же состояния, что и автор).
В чисто практических терминах разница в этих подходах в том, что на загрузку в мозг математики требуется время: надо взять и по кирпичикам выстроить в голове ту же семантическую сеть, которая декомпозирована автором в отдельные определения на бумаге (но зато можно быть уверенным, что коммуникация будет (почти) однозначной и сеть получится (почти) точно такой же); музыка же или (в меньшей мере, но все же) поэзия воспринимаются конкретно, прямо сейчас, потому что дергает уже существующую семантическую сеть (или не дергает, если автор промахнулся с ожиданиями относительно читателя - то есть это такой lossy compression).
Как это все относится к программированию? Казалось бы, очевидно, что математический путь нам должен быть ближе - но! На самом деле цели взаимодействия с построенной системой у разработчика и математика (или студента, например) разные: нам не нужно загружать всю систему себе в голову, нам (обычно) нужно локализовать какой-то конкретный дата\контрол флоу, внести какое-то локальное изменение, найти дающие нужный нам результат входы в систему. Никто не изучает легаси просто ради того, чтобы любоваться спрятанными в нем прекрасными структурами и абстракциями (хотя ладно, наверняка и такие мазохисты существуют); обычно нам нужно (не имея полной картины!) взять и сделать, чтоб работало (и притом еще вчера).
И вот здесь как раз в дело вступает контекст: это и соглашения по именованию, и общие идиомы, и паттерны, и распространенные архитектуры - наконец, мы пытаемся буквально использовать метафоры из реальной жизни (по сути весь ООП (был) построен на этой посылке (спойлер: такие метафоры обычно не работают)). Если X на самом деле Y, но очень похож на Z, то зачастую мы назовем его Z - просто потому, что это сэкономит время 95% юзеров, которые будут использовать его именно в этом качестве (ну, или это мы сейчас думаем, что будут).
В итоге, конечно, важны оба подхода - первый все-таки в большинстве случаев предпочтительнее, но может плавно перетекать во второй; надо стремиться именно к выработке общего словаря оригинальных терминов и смыслов, к некоей общей "теории дизайна систем", которая будет включена в общий контекст индустрии. И еще было бы неплохо отвязаться от английского языка как эдакого общего знаменателя для всех обозначений: в идеале, для идентификаторов нужен легковесный сконструированный язык на основе общий латинских (по факту - давно интернациональных) корней с гибкой грамматикой и простым словообразованием. И еще пара идей:
- Меньше LoC - меньше возможных багов. Меньше имен - меньше проблем с их придумыванием. Если вместо введения нового имени можно тривиально и очевидно соединить уже имеющиеся, то лучше так и сделать. В этом плане любопытен APL и tacit programming вообще (дисклеймер: я не предлагаю писать рейтрейсеры в 8 строчек на j и прочие прелестные извращения):
+
- сложить числа,/+
- суммировать список, и так далее. По факту имя является реализацией, а потому абсолютно точно, понятно и однозначно. Это хороший идеал (но непонятно, как его достичь). - Туда же: явное лучше неявного. Structural лучше, чем nominal. Композиция лучше, чем комбинаторный взрыв. Open maps лучше, чем records и javabeans. Если можно вместо именования данных указать сами данные, то лучше так и сделать.
- Если
let <имя> = <функция> 42
понятней, чем просто<функция> 42
, то это плохая функция. То же и с аргументами. В идеале, функция должна однозначно определять свои аргументы и результат.numToWhichWeAdd = 1, numThatWeAdd = 2, numToWhichWeAdd.plus(numThatWeAdd)
не понятнее, чем1 + 2
. Если от введения временных переменных перед передачей их в функцию код становится понятнее, чем при прямой передаче аргументов без временных имен, то с кодом что-то не так. Если функция берет больше 2-3 аргументов, с функцией что-то не так. - Паттерны нужно формализовывать и называть уникальными именами. Нужно больше монад и трансдьюсеров (но только если все о них знают).
- Не бояться придумывать новые слова.
aeThaex3
Очень хорошие мысли, в целом разделяю это + несколько комментариев по ходу:
Просто в дополнение: а adoption языков "математического" подхода в целом ниже. Возможно от того, что это следствие образа мышления как пишущих код, так и людей, чьи требования формализуются и тех, кто их формализует; возможно, следствие исторического развития прикладных систем большей частью "снизу", "от железа"; а еще возможно, этого "общего контекста индустрии" просто не существует: "write once run everywhere" регулярно возникает и по большей части фейлится.
+1. Это очень важный момент для меня. Постоянно вспоминаю, что нам обещали с ligthtable (хотя может я тот видео-концепт понял по-своему) и хочу сделать что-то, что поможет увидеть в системе реальный execution flow системы имея конкретные данные на входе. (Может есть такие системы?) Также это должно сделать сверхэффективным прогон acceptance тестов. Change control flow -> run only tests which implicitly touch changed logic flow-node. И связанная идея, чтобы рантайм хоть немного осознавал себя (also see https://github.com/razum2um/cljsh)
И еще немного адвокатства дъявола, just fyi
По-моему, именно loc слабо коррелирует с именованием (за исключением случаев, когда style guide вынуждает из-за длины строки) и числом имен (кроме случаев, когда point-free style сочетается с тем что assignment/binding != expression, как в python/js). Возможно, лучше говорить про соотношения объемной доли облачка на строчку. Забавный флешбек из прошлого http://razum2um-gentoo.blogspot.com/2010/04/python-one-line-scripts.html
С одной стороны отлично понимаю, что имеется в виду и согласен, но довести до абсурда, то в каждом имени должны просвечивать имена регистров итд.
Опять же понимаю и согласен, но с другой стороны, для полноты картины такой случай: есть (a + b) / (c - d) и есть sum / diff. Это же как раз пример где Structural лучше, чем nominal. По факту, скобки в первом как раз намекают на структуру, но оно остается nominal
Кстати, мне кажется, противоречие - nominal это как раз явное, а любая структура/иерархия -> абстракция/неявность