Найти тему
ZDG

Старый программист рассказал чего боится, семья не сдержала слёз

Оглавление

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

Очевидно, что все начинают с разного. На одном конце спектра находятся те, кто застал ЕС ЭВМ и их первым языком был ассемблер или Фортран. На другом – молодая поросль, которая только что прошла курсы Питона или Go. Также есть специалисты, которые не теряли время на пустяки, а сразу освоили Haskell и находятся в нирване.

Первый опыт очень важен. Он даёт базу, формирует предпочтения, привычки и типовые методы, которыми мы решаем задачи.

Например, работает 50-летний программист. За его плечами грохочет славное время программирования на ассемблере и C, но сейчас он пишет сайты на Битриксе. Если с ним заговорить про ООП, то можно будет услышать авторитетное мнение – да ну его, это ООП, фигня какая-то.

С другой стороны, работает 20-летний программист. Вчера он узнал, что появилась новая модная версия языка, в которой есть лямбды, кложуры, корутины и промисы, и поэтому он переписал проект с лямбдами, кложурами, корутинами и промисами. Заговорите с ним про ООП, и он авторитетно заявит, что ООП это отстой, сейчас модно редукс шмедукс дваноль, который ставится через композер нпм ярн бабел грунт гулп ант гхеркин градле (падает от сильного удара по голове).

-2

И то и то – тревожный такой звоночек.

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

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

Предел развития

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

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

-3

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

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

И конечно, потребовались усилия, чтобы из неё выйти.

ООП

Долгое время я игнорировал ООП. Но это было не только из-за моей лени. На старых компьютерах объём и быстродействие кода играли важную роль. Несколько лет я писал исключительно на ассемблере. Иногда мне казалось, что будет проще использовать язык высокого уровня. Но стоило сравнить программу, написанную на ассемблере, с написанной пусть даже на лаконичном C++, и количество лишних и бессмысленных операций приводило в ужас. Мне это доставляло почти физическую боль, даже если никак не влияло на результат. И я возвращался к ассемблеру.

Примерно такая боль
Примерно такая боль

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

Сейчас ООП для меня основной инструмент, и я скорее буду писать программу с ООП, чем без. Это реально удобно.

Тернарный оператор и форматирование кода

Во многих языках есть такая конструкция:

foo = bar > 0 ? 1 : 0;

Она аналогична такой:

if (bar > 0) foo = 1; else foo = 0;

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

Рассмотрим случай, когда переменную надо создать и сразу же инициализировать:

int foo = 5;

Но если инициализация зависит от какого-то условия, придётся писать так:

-5

Почему не в одну строчку? Потому что долгие внутренние диалоги в конце концов убедили меня, что так делать правильнее. Как бонус, больше не надо думать о том, одиночная инструкция или нет.

Но это порождает слишком много строк кода на одну инициализацию переменной.

Тернарный оператор позволяет всё это обойти. Переменную можно объявить и в этой же строке присвоить нужное значение, не помещая её в блок и не нарушая выбранный стиль написания кода:

int foo = bar > 0 ? 1 : 0;

Аналог из Питона:

foo = 1 if bar > 0 else 0

Аналог из Perl (без else):

my foo = 1 unless bar < 1

Так что это не просто чей-то мозговой выверт, а средство упаковать условие в одно выражение, которое можно чему-то присвоить или куда-то передать.

Пользоваться им я начал, и довольно-таки активно.

Анонимные функции (лямбды)

Как человек, воспитанный Паскалем (да-да), я привык к стройной и строгой структуре программы. Вот функции, вот данные, вот программа. Функции описывались заранее и навсегда, и никак не предполагалось, что они могут возникать из ниоткуда или уходить в никуда.

В JavaScript анонимные функции можно объявлять как свойство объекта:

-6

И передавать как аргумент в другие функции:

-7

JS-то бог с ним, но такие вещи нашлись или впоследствии появились в других языках. Воспринимать их было просто дико, как если бы кто-то занимался непотребствами у вас на виду. Создать функцию прямо в параметрах вызова другой функции? Кто вас научил этой гадости? Я вызываю полицию Тьюринга!

-8

Корни надо искать в функциональном программировании и лямбда-исчислении. Конкретно слово "лямбда" стало ассоциироваться больше с простыми функциями, которые можно записать в одну строку, и под них даже сделали специальный упрощённый синтаксис. Вроде того, что пишем не так:

function(x) { return x * 2; }

а вот так:

x => x *2

Ну понятно, что это функция, а x это аргумент, а x *2 это результат, поэтому не надо писать function, скобки и return.

Самое обычное применение для них это обработка массивов. Например, если в одном массиве содержатся числа, а в другой надо положить эти числа, умноженные на 2, то обычный код будет выглядеть так:

-9

Более модный код будет выглядеть так:

foo = [1, 2, 3, 4, 5];
bar = foo.map(num => num * 2);

Здесь используется функция map(), которая отображает значения массива foo на значения массива bar с помощью лямбды, которая превращает аргумент num в результат num * 2.

Само использование функции map(), наряду c reduce() и filter(), меня отвращает до сих пор. Мой мозг всегда отметает эти варианты, будто их нет. Единственное исключение, это когда важна скорость и map() работает быстрее.

Замыкания (closures)

Замыкания это продолжение концепции анонимных функций. Рассмотрим пример:

-10

Функция create_closure() создаёт и возвращает нам другую, анонимную функцию. Эта функция имеет аргумент y, а возвращает результат в виде x + y. При этом параметр x объявлен как локальная переменная в функции create_closure() и она же глобальная по отношению к анонимной функции.

Так вот, когда возвращается анонимная функция, она возвращается вместе с контекстом, где для неё, и только для неё, существует эта самая переменная x.

Вызвав эту функцию с параметром y = 10, мы получим результат 15, т.к. x = 5. Называется это всё замыканием, потому что среда выполнения функции как бы замыкается, изолируется от внешнего мира, сохраняя при этом свои локальные данные.

Рассказал я это всё для того, чтобы сообщить, что меня это отвращает и я никогда этим не пользовался (ну, исключение это JS, так как там иначе бывает нельзя).

Почему-то нейросеть выдала эту картинку на запрос Closure. Ну, пусть будет.
Почему-то нейросеть выдала эту картинку на запрос Closure. Ну, пусть будет.

А почему, собственно, отвращает? А потому что я воспитан Паскалем. Я хочу точно знать и контролировать, где какая переменная хранится и что с ней происходит. А здесь получается какая-то дикая помойка.

Обещания

Это не название песни, а конструкт Promise. Суть здесь вот в чём. В JavaScript, к примеру, мы загружаем какую-то картинку. Дальше буду писать некий обобщённый код, потому что уже сам устал, и все устали. Типа:

var img = load('logo.png');
var w = img.width;

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

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

img.onload = function() { w = img.width; }

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

Это довольно неудобно.

Обещания я бы назвал синтаксическим сахаром для подобных действий, хотя их функционал вообще-то шире. Описывать их в виде кода не буду, чтобы этим самым продемонстрировать, что я ещё их не освоил. Это место, где я ещё должен развиться.

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

Но я стараюсь, Ринго, изо всех сил стараюсь.