Но скорее всего во многом потому что почти не программировал.
Новички часто пытаются понимать программу как процесс исполнения каждого символа:
символ -> подстановка -> результат -> следующий символ
..
5. Поиск «фаз подстановки» в C — интересный симптом
Представьте, что вам дают функцию на C:
int sum(int *a, int n) {
int s = 0;
for (int i = 0; i < n; i++)
s += a;
return s;
}
Что происходит первым?
Вы сразу видите:
«Она суммирует массив».
Или вы начинаете разбирать:
цикл, индекс, память, возврат значения, шаги исполнения...
Если у вас чаще происходит второе, то проблема может быть не в том, что Tcl научил неправильной модели, а в том, что вы недостаточно часто формируете сжатые смысловые представления кода и слишком долго остаетесь на уровне деталей.
Тогда Tcl мог быть лишь средой, где такая склонность удобно закреплялась. Но источник был бы глубже и не сводился бы к особенностям самого языка.
У меня были совершенно жуткие и неприятные ментальные модели Lisp, где скобки ели другие скобки уменьшаясь на каждом шаге.
Это уже звучит не как типичная «лисповская» модель, а как очень буквальная визуализация процесса вычисления.
Когда многие люди смотрят на Lisp-код:
(+ (* 2 3) 4)
они либо видят дерево:
+
/ \
* 4
/ \
2 3
либо мысленно вычисляют:
(* 2 3) -> 6
(+ 6 4) -> 10
А ваша модель:
скобки ели другие скобки, уменьшаясь на каждом шаге
напоминает скорее визуализацию переписывания текста или редукции выражения как физического процесса.
То есть не «структура программы» и даже не «вычисление значения», а буквально некая анимация трансформации записи.
Интересно, что это не похоже на то, чему обычно учит Tcl.
В Tcl скорее возникает модель:
puts [expr {$x + 1}]
как последовательность подстановок:
[expr {$x + 1}]
↓
11
↓
puts 11
А модель со «скобки пожирают скобки» напоминает что-то более общее: стремление представить вычисление через геометрический или механический процесс.
Я бы даже сказал, что в вашем описании меня больше цепляет слово «жуткие».
Не просто необычные или неудобные, а именно неприятные.
Если так, то я бы не формулировал проблему как «Tcl испортил мышление».
Скорее:
При попытке понять программу я вырабатывал чрезмерно конкретные визуальные модели вычисления.
Причём такие модели могут возникнуть практически на любом языке.
На Lisp — скобки схлопываются.
На Tcl — происходят подстановки.
На C — значения «текут» по памяти или выполняются по шагам.
На машине Тьюринга — головка бегает по ленте.
На самом деле любая вычислительная система допускает такую визуализацию.
Любопытно, что когда опытный программист видит большой кусок кода, он часто вообще не визуализирует вычисление целиком. Он может хранить в голове очень абстрактное описание:
это парсер;
это обход дерева;
это кэш;
это конечный автомат.
И только при необходимости «приближает камеру» до уровня отдельных шагов.
Из вашего описания складывается впечатление, что камера у вас по умолчанию была сильно приближена, причём иногда даже к процессу редукции синтаксической формы, а не к задаче, которую решает программа. Это действительно может сильно затруднять чтение кода, но это более общая когнитивная привычка, чем что-то специфическое для Tcl или Lisp.
Представьте, что вам дают функцию на C:
int sum(int *a, int n) {
int s = 0;
for (int i = 0; i < n; i++)
s += a;
return s;
}
У меня очень небольшой опыт на си и программирования вообще, но мысли похожи на:
int, значит наверное переменная, sum - имя, открываются скобки я в контексте этой переменной, нужно внутренне увеличить контекст на 1, указатель.
Эти функции вместе делают это...
Вот это описание уже очень показательно.
То, что вы описали, не похоже на «понимание функции sum». Это похоже на попытку одновременно отслеживать синтаксическую структуру текста.
Вы видите:
int sum(int *a, int n)
и внимание сразу уходит в:
int — что это значит;
sum — имя;
( — новый контекст;
*a — указатель;
ещё один аргумент;
закрывающая скобка.
То есть вы читаете программу почти как грамматику.
Многие начинающие действительно так читают код. Это нормально на ранних этапах, потому что язык ещё недостаточно автоматизирован в голове.
Опытный C-программист обычно воспринимает эту строку гораздо более крупными блоками:
функция sum, принимает массив и размер, возвращает целое число.
Он уже почти не замечает отдельные токены.
Есть известный эффект в обучении чтению.
Ребёнок сначала читает:
К - О - Т
КОТ
Потом начинает видеть слово целиком.
Потом фразу целиком.
С кодом происходит похожее.
Пока конструкции не стали знакомыми, мозг вынужден разбирать их по частям.
Например, для человека с опытом:
for (int i = 0; i < n; i++)
часто воспринимается почти как один символ:
пройти по всем элементам.
Он даже не думает про три секции цикла.
Поэтому я бы осторожно относился к идее:
Tcl научил меня плохому мышлению.
Из того, что вы сейчас написали, я вижу более простое объяснение:
при чтении кода слишком много внимания уходит на синтаксис и локальную структуру записи.
Это может происходить независимо от языка.
Интересно другое. Вы написали:
открываются скобки я в контексте этой переменной, нужно внутренне увеличить контекст на 1
Это довольно необычная формулировка.
Большинство людей сказали бы:
открываются скобки — начинаются параметры функции.
А вы описываете почти пространственную операцию:
увеличить контекст на 1.
То есть у вас есть внутренняя модель вложенности, уровней, состояний разбора.
Это действительно напоминает взгляд через призму парсера.
Но даже здесь не очевидно, что причина — Tcl. Многие люди с математическим или системным складом ума самостоятельно начинают видеть программы как вложенные структуры и состояния.
Меня бы заинтересовал такой эксперимент.
Когда вы видите:
return s;
что происходит?
Вы сразу понимаете:
вернуть накопленную сумму.
или
Возникает что-то вроде:
оператор return, теперь управление выйдет из функции, значение будет помещено в место вызова...
Если ближе второй вариант, то ваша модель действительно сильно ориентирована на механизм исполнения. И тогда проблема может быть не в том, что модель неправильная, а в том, что она включается слишком рано и слишком подробно. Вы начинаете анализировать внутренности конструкции ещё до того, как сформировалось понимание её роли в программе. Это очень затрудняет чтение кода, особенно длинного.
Это может означать, что модель была очень навязчивой: вместо того чтобы помогать понимать код, она перехватывала внимание.
Синестетически неприятна как воронки или как хищник - жертва.
Это интересное уточнение.
Если вы говорите не просто о метафоре вычисления, а о том, что визуализация была синестетически неприятной, ассоциировалась с воронками, поглощением, хищником и жертвой, то это уже не совсем вопрос о языках программирования.
Тогда получается примерно такая картина:
Вы читаете Lisp-код.
Мозг автоматически строит образ редукции выражения.
Этот образ имеет эмоционально-сенсорную окраску.
Причём окраску неприятную, связанную с поглощением, исчезновением, схлопыванием.
То есть проблема не в том, что вы рационально решили представлять вычисление через пожирание скобок. Скорее, такая ассоциация возникала сама и навязывала определённое восприятие.
Заметьте, что сама идея редукции в Lisp объективно существует:
(+ (* 2 3) 4)
↓
(+ 6 4)
↓
10
Но большинство людей воспринимают это нейтрально.
Для них это просто преобразование выражения.
У вас же, судя по описанию, преобразование получало дополнительный образный слой:
одна структура поглощает другую,
что-то исчезает,
что-то схлопывается.
Именно этот слой и мог быть неприятным.
Я бы даже сказал, что это может объяснять часть трудностей с кодом лучше, чем гипотеза про Tcl.
Если при чтении программы автоматически возникают очень яркие пространственные или сенсорные модели, то они могут занимать значительную часть внимания.
Тогда чтение кода превращается не только в анализ логики, но и в обработку целого набора внутренних образов.
При этом я не вижу причин считать, что Tcl вызвал такую реакцию.
Скорее язык просто дал материал для уже существующей склонности мозга строить очень конкретные модели процессов.
На одном языке это могли быть подстановки.
На Lisp — поглощающие скобки.
На другом языке это могут быть потоки, контейнеры, слои памяти, узлы графа и т.д.
Меня ещё заинтересовало, что вы употребили слово «синестетически».
Если использовать его не строго медицински, а в широком смысле — как смешение абстрактных понятий с пространственными, визуальными или эмоциональными качествами, — то тогда ваши описания выглядят не как ошибки понимания программирования, а как особенности того, как у вас вообще формируются внутренние представления.
То есть вопрос может быть не:
«Научил ли меня Tcl плохой модели вычисления?»
а скорее:
«Почему при попытке понять абстрактную систему у меня возникают настолько конкретные и эмоционально окрашенные образы?»
return s;
return, значит вернуть результат, это аргумент, следующее это результат
Это, кстати, звучит вполне нормально.
Когда вы говорите:
return, значит вернуть результат, это аргумент, следующее это результат
я не вижу здесь чего-то патологического или необычного. Наоборот, это уже довольно близко к смыслу конструкции.
Если бы вы сказали:
return вызывает завершение текущего activation record, значение копируется согласно ABI, потом управление передаётся по адресу возврата...
то это было бы примером ухода в механику исполнения.
А здесь вы фактически сворачиваете конструкцию до её назначения:
вернуть s.
Меня в вашем предыдущем примере больше удивило другое:
открываются скобки я в контексте этой переменной, нужно внутренне увеличить контекст на 1
Это гораздо дальше от смысла программы и гораздо ближе к отслеживанию структуры разбора.
То есть у вас, похоже, есть смесь двух режимов:
Для знакомых конструкций
Вы можете читать относительно семантически:
return s;
↓
вернуть результат.
Для менее знакомых конструкций
Внимание переключается на синтаксис:
int sum(int *a, int n)
↓
имя, скобка, контекст, указатель, уровень вложенности...
Именно поэтому я не уверен, что проблема в какой-то «испорченной модели программирования».
Из ваших ответов скорее складывается впечатление, что у вас ещё не очень много автоматизированных шаблонов чтения кода.
Когда шаблон знаком, вы видите смысл.
Когда шаблон незнаком или сложен, мозг начинает опираться на более низкий уровень — токены, скобки, контексты, вложенность, механизм.
Это довольно обычный путь обучения.
Что касается тех образов про Lisp, там мне кажется важным различать две вещи:
Как вы понимаете код.
Какие образы автоматически возникают.