Арка 1

Глава 1. Вселенский секрет суперуспешного программиста

Ещё задолго до появления в нашей жизни всех законов разработки ПО, ещё до того, как мы досконально определим, что хотим программировать, ещё до того, как мы разберёмся с компьютерными науками, существовал (и продолжит существовать) вселенский секрет, который определяет разницу между старшим программистом,который учит новый язык программирования за день, и "вечным джуном", который пять лет пилит микросервисы без повышения на работе. Этот секрет отделяет хреновых программистов от хороших, хороших от великих, и великих от рок-звёзд — тех рок-звёзд, что делают большие бизнесы.

Этот секрет не является чем-то запредельно сложным для понимания, он не требует родиться с определённым талантом или же жертвовать свою кровь Сатане. Там в принципе ничего сложного нет, но, тем не менее, этот секрет, скорее всего, так и останется сокровенной загадкой на долгие века, и только избранные могут поставить его себе на службу, ибо для него требуется колодец вечности, в простонародье "жопочасы". Вот он:

Чем лучше вы понимаете то, что вы делаете, тем лучше у Вас выходит это делать.

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

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

Однако по другую сторону баррикады стоят ошеломлённые обилием информации ребят. Я тоже вхожу в число программистов, которые озадачены обилием информации, равно как в этом признаётся Макс Канат-Александр, которого я вольно перевожу в этой главе. Чтобы делать что-то хорошее, нужно так много всего нужно знать, что можно было бы буквально потратить остаток своей жизни на изучение и все равно получить только от силы треть (33%) всех данных, которые нужно знать о компьютерах, чтобы уметь проектировать всё что хочешь. В оригинале у Макса звучала цифра 90%, но статья писалась за 11 лет до этого, и тогда ещё был излишний оптимизм в этом плане.

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

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

Вы уже видите, в чём подвох? Те, кто читал "Мифический человеко-месяц" Ф. Брукса, те знают, что нельзя просто так взять и измерить в условных трудоднях время создания программы. Один выдающийся программист может спокойно взять и сделать вам за пару недель такую систему, которую сотня посредственных программистов будет пилить год, а на менеджеров вы потратите сумму, равную сумме ещё пары сотен кодеров.

Абсолютно то же самое можно применить и к самому изучению программирования. Нельзя просто так взять и сказать "прозанимайся десять тысяч часов, и будет тебе счастье". Нет, это так не работает. Я тут сошлюсь к текущей практике, где людей на должность рассматривают исходя из их стажа работы. Эта практика очень далека от идеала, но, с другой стороны, если человек пару лет пилил микросервисы и микрофронтенды и пихал всё это в кубернетес, потом его контора разорилась в связи с кризисом, так вот — есть шанс, что этот человек чему-то научился. И, вместе с другими своими коллегами по несчастью, может запилить тебе если не суперкорпорацию, то хоть сытный (пока не закончатся деньги западных инвесторов) свечной заводик.

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

Глава 2. Почему у нас программисты хреновые

Начнём мы с ещё одного вольного пересказа с моими достаточно большими дополнениями Макса Канат-Александра, на этот раз "Why Programmers Suck", или же по русски "почему программисты отстойные". В общем, всех нас достала ситуация, что современные программы, мягко говоря, хреновые. Они мало того, что жрут ресурсы как не в себя — в конце концов, то, что я не могу на ноутбуке всего лишь десятилетней давности запустить нынешнюю версию (2022) скайпа и чтобы она не глючила, хотя скайп 2011 года в своё время работал на нём нормально, — так вот, с этим можно смириться, в конце концов, сейчас требования к софту и к железу постоянно возрастает, и вот нам уже мало fullhd видео, а хочется 4k. Нет, проблема не в этом.

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

Макс говорит, что он долгое время не знал, почему программисты позволяют себе писать хреновый код. Он, как и многие другие, пребывал в плену стереотипа, что "плохие программы делаются плохими программистами", и что достаточно описать паттерны и принципы разработки программ, чтобы преодолеть эту ситуацию. Как он говорит, "the truth was beyond my imagination", то есть "правда превзошла мои возможности её представить".

Подавляющее большинство (90% или более) программистов абсолютно не представляют, что они делают.

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

К сожалению, я, Рева Денис, тоже не избежал участи попасть в это число. Могу вспомнить как я делал CMS на основе keystonejs и как просто скопировал и менял в паре мест дефолтные конфигурационные файлы. И как прикручивал импорт страниц из соцсети VK с помощью библиотеки vk-io. Что же, попробую исправиться.

Так почему это происходит? В чем проблема? Как могло появиться так много людей, которые совершенно не понимают, что они делают?

Что ж, это звучит немного так, как будто они какие-то «глупые». Но что такое глупость? Люди не глупы просто потому, что чего-то не знают. Есть много вещей, которые не все знают. Это не делает их глупыми.

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

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

Зачем я пишу эти гуманитарные тексты? Почему бы просто не написать в очередной раз о синтаксисе Хаскеля, чтобы вы могли скорее начать составлять из него заклинания? А остальное — забота компилятора?

Компилятор, к сожалению, не позаботится ни о тебе, ни обо мне. Так что нам придётся долго и упорно изучать даже синтаксис, а сам подход к разработке и к моделированию. Да, без сомнения, я, кажется, раздуваю этим объём текста, который нужно прочитать — всегда можно, умолчав о некоторых подробностях, сократить объём текста. Вплоть до бесполезных слов наподобие "монада — эндофунктор в категории изоморфизмов".

Этим не только программисты страдают. Я уверен, что у коллег химиков или физиков-ядерщиков тоже постоянно присутствуют подобные проблемы — знать по идее нужно много, а идею "учиться от забора и до заката" организм не приветствует.

Однако иногда очень трудно понять, что изучать. Компьютерных наук просто слишком много, да и тот же хаскель, по сути, тоже ни разу не минималистичен по объёму знаний. Причём — это моё наблюдение — зачастую с техническими знаниями может всё быть в принципе в порядке, но не хватает частей, которые связывают разрозенные знания по Линуксу, Докеру, JavaScript'у, почтовым серверам, базам данных в единое целое. То есть, не хватает гуманитарного клея.

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

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

Мда, вам не кажется, что мы куда-то не туда зашли с этой мрачностью? Я ещё неоднократно вернусь к теме мотивации. Самое главное, если вам кажется, что у вас ничего не получается, если вы продолжите упорствовать и будете разбираться, у вас всё получится. Здесь другие авторы разместили бы слова о том, что "нужно обращаться в комьюнити", но комьюнити (сообщество) хаскеля токсичное, поэтому я не советую. Просто прогуляйтесь на улице, сделайте какие-нибудь физические упражнения для тела. И пока вы ещё окончательно не устали, мы изучим ещё один секрет от Макса Канат-Александра, который нам поможет начать на хаскеле не просто кодить, а кодить быстрее. И, в конечном счёте, мы сможем учиться быстрее, пусть и объём программы от этого резко возрастёт. Это секрет зовётся "не думать".

Глава 3. Секрет быстрого программирования — перестать думать

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

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

Эти рассуждения, конечно, хорошие, но как нам стать быстрее? Продать душу Сатане? Что же, как я, Рева Денис, как загримированный сатанист говорю, что Сатане проданные души, как правило, не требуются, — слишком слабые, и цены нормальной не имеют.

Вспоминаем два предыдущих высказывания:

  1. Чем лучше вы понимаете то, что вы делаете, тем лучше у Вас выходит это делать.

  2. Подавляющее большинство (90% или более) программистов абсолютно не представляют, что они делают.

И вот он ответ: чтобы стать быстрее, нужно понимать, что мы делаем в каждом конкретном случае. Что же, отчасти новость для нас печальная: никаких тебе ранних священных войн между Final Tagless и Free monads и прочих красивостей, для которых нужно обладать большим предварительным опытом. В общем, сложность как раз в этом. Нам нужно понять, как мы можем ускорить наш рост как разработчиков — понять ещё до того, как мы, собственно говоря, столкнёмся с проблемами роста.

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

Но всё таки, что в чём дело? Макс выводит следующую закономерность:

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

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

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

А если из таких непонятных слов или символов состоит вся программа? Нужно ли удивляться, что вчера я в своей DevOps-практике потратил три часа на починку кеширования redis в сервисе, написанном на python'е, где было банальное добавление поддержки аутентификации. Там достаточно было три строчки дописать. Причем в, как мне сейчас кажется, достаточно очевидных местах.

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

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

У меня есть определённая проблема — я не уверен в том, что мою книгу следует публиковать. И следуют ли её публиковать вообще. Не поднимите ли вы меня на смех? Обвините в краже каких-то идей? Хотя, буду честен, без заимствований написать что-то вменяемое просто невозможно.

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

Глава 4. Записать всё в подкорку

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

Как вам это удаётся? Ну, эти знания глубоко забиты в ваш мозг, у некоторых — буквально. Здесь хорошо подходит нелюбимое мой попсовое слово "подсознание". На самом деле навыки возникают на границе первой и второй сигнальной системы, но лучше оставим эту тему на потом. Сейчас — выжимка.

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

Чем больше ассоциаций, тем проще в конечном счете составить из слов предложение. Чем больше ассоциаций с необходимым нам предметом, тем больше смысл сказанного будет более точно описывать этот предмет: вплоть до "Денис, ты переусложняешь! Когда уже будет синтаксис хаскеля?". Нет, это только кажется сложным.

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

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

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

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

В цирке выступали 4 большие собаки и столько же маленьких. Сколько всего собак выступало в цирке?

На псевдокоде это будет кодироваться так вот:

большиеСобаки = 4

маленькиеСобаки = большиеСобаки

количествоСобакВЦирке = большиеСобаки + маленькиеСобаки

Вот так вот всё просто на самом деле: мы просто переходим в более абстрактную форму.

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

Глава 5. На самом деле я мог просто пересказать Дениса Шевченко

На самом деле я мог просто пересказать в этом месте моего тезку Дениса Шевченко. У него великолепное объяснение функций для начинающих в его книге "О Haskell по человечески" (https://www.ohaskell.guide/). И я, наверное, всё равно сделаю что-то похожее.

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

Haskell-программа представляет собой выражения (expressions), которые образуют функции (functions):

double x = x + x — функция

x + x — выражение
double x — объявление функции.
= знак равенства, отделяет объявление от выражений.
x между double и знаком равенства — аргумент функции.

Всё, в принципе, просто. Поехали решать ту же задачу за второй класс:

В цирке выступали 4 большие собаки и столько же маленьких. Сколько всего собак выступало в цирке?

Можно напрямую перевести в код, если мы соединим функции:

bigDogs = 4 -- количество больших собак в цирке
smallDogs = bigDogs -- количество маленьких собак в цирке
countDogsInCourt = bigDogs + smallDogs -- собак всего

Считаем в голове: 4 больших + 4 маленьких => 4 + 4 = 8. Отлично!

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

Следующая задачка из 2 класса:

У любознательного Читайкина в библиотеке на первой полке 11 книг, а на второй на 2 книги больше. Сколько книг на второй полке?

Решаем:

booksOnFirstShelf = 11
booksOnSecondShelf = booksOnFirstShelf + 2

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

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

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

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

Типы можно вложить друг в друга: мы можем сказать, что тип "собака" включен в общий тип "животные", а "животные" — тип "живые существа".

Haskell, на самом деле, весь о типах. Именно возможность создать больше типов, чем это принято в мейнстримных языках программирования, и определяет его сложность к изучению и вместе с тем его гибкость. Как писал мой упоминавшийся выше тезка:

Тип — это Второй Кит в Haskell. Тип отражает конкретное содержимое данных, а потому все данные в программе обязательно имеют некий тип. Когда мы видим данное типа Double, мы точно знаем, что перед нами число с плавающей точкой, а когда видим данные типа String — можем ручаться, что перед нами строки.

Типы (вместе с уже знакомым Вам определением функции) описываются примерно так:

Умножающая функция:

multyply :: Int -> Int -> Int
multyply x y = x * y

:: — двойное двоеточие указывает на тип
Int — тип целого числа
-> — порядок преобразования типов

Стрелки "->" работают хитрым образом. Самый правый тип за последней стрелкой указывает тип, который имеет результат функции, а остальные типы — определяют аргументы функции. К примеру у разбираемого у Дениса Шевченко объявления

square :: Int -> Int
square x = x * x

На вход идёт аргумент x с типом Int, и на выход — Int. Сложные случаи, когда разные типы… ну, мы люди простые. Аргументы могут быть и именованными особо тщательно, мы это уже делали:

triangleArea :: Float -> Float
triangleArea side height = side * height / 2

Здесь мы вычисляем площадь треугольника, на этот раз с типом Float "плавающая точка". Удобнее, когда аргументы подписаны, согласитесь?

И да, зачем я всё-таки пересказал Дениса Шевченко, если я мог просто его скопировать? Там даже лицензия позволяет это сделать, единственное, с некоммерческими ограничениями.

Ответить на этот вопрос представляю вам самим.