• Не смотрю и не слушаю

    Не помню, говорил или нет: я не слушаю подкасты, не смотрю познавательные видео, сериалы, интервью Дудя, выступления Шульман и всех остальных. В Телеграме состою только в профильных чатах (программирование, кложа, джава). В RSS-читалке почти ничего не осталось — отписываюсь. Уж если начал признаваться, то вот еще — почти ничего не смотрел из Рича Хикки.

    Это не потому, что я такой продуктивный и эффективный. Когда работа надоедает, я как и все туплю в интернет — читаю популярные ресурсы, Википедию, смотрю Ютуб, словом — трачу время, которое мог бы использовать на что-то созидательное.

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

    Недавно я смотрел выступление Максима Ильяхова. Он выступил прекрасно: манера речи, мимика, интонации, все это так живо и смешно. Но что он говорил — я не смог вспомнить уже через 15 минут. Это не Максим плохо выступил, это я такой. Поэтому отдаю отчет — тот просмотр был развлечением, попыткой увильнуть от дела.

    С годами мне все более очевиден факт — запоминаешь только то, что мысленно пересказываешь и пробуешь на практике. Третьего не дано: никакие видео, короткие или длинные, никакие подкасты не продвинут вас так далеко, как пересказ и практика. Конечно, и от подкастов есть польза, но если выбирать средство с малой пользой и большой, то есть же разница, верно?

    В моем понятии видео прочно связано с развлечением. Когда я вижу браузер и Ютуб, ни о каком обучении не может быть и речи. Так я воспитан: раньше вся информация была в виде текста (книги, учебники). Возможно, у молодежи дела обстоят по-другому, но как отец я знаю: ребенок, севший за комп “посмотреть лекцию по географии”, быстро переключается на мемы про Майнкрафт.

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

    Эти убеждения долго зрели в моей голове, пока я не прочел “Думай как математик” Барбары Уокли. Книга затянута, можно было бы выкинуть треть, но ради некоторых мест стоит потерпеть. Барбара и ее коллеги высказывают мысль — чтобы что-то запомнить, нужно прочесть материал и многократно его пересказывать. Если что-то забыли, то ни в коем случае не читать, а потратить минимум пять минут на вспоминание. Это важнейший процесс мозга, который нельзя прерывать. Даже если вы не вспомнили материал, уже сам процесс выстраивает нужные связи, и при следующем чтении материал запомнится лучше. И конечно, любые знания нужно закреплять практикой.

    Теперь скажите мне, как вы смотрите подкасты? Вы их потом вспоминаете, пересказываете, да? Прослушав подкаст о Кложе, бросаетесь писать на ней? Ведь нет же. Ничего не закрепляется. Все это развлечение. Я не ставлю это в вину, пусть каждый развлекается как хочет, но не обманывайте себя, будто чему-то учитесь. Это то же самое, что миф о 25 кадре или обучении во сне с плеером.

    Вспоминаю, как в молодости увлекался 3D-моделированием. Интернет был никакой, поэтому заказывал по почте видеоуроки. Со временем понял, что смотрю их как футбол: другой человек работает, а ты бездумно смотришь с бутербродом в руке. Когда пытался повторить один из уроков — слепить простенького динозавра — обалдел от того, что ничего не могу. Что, помогли многочасовые просмотры уроков?

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

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

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

    Наконец, меньше всего я хочу быть тем, кто пересказывает интернет. С отдельными людьми я почти не разговариваю, потому что знаю — все их мысли заимствованы из Ютуба и Телеграма. В этом легко убедиться: уже на второй реплике тебе кидают ссылку с “пруфом” — безусловно правдивой информацией, которую человек только что пересказал.

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

  • Страсть к делу

    Эта мысль тоже не понравится никому.

    Часто встречаю комментарии в духе: сейчас такой стресс, целый день читаю новости, не могу работать. Как можно работать в такое время!

    Тот, кто это пишет, недостаточно любит свое дело. Да, обстрелы. Да, новостная лавина. Но уверяю, когда страстно что-то любишь, никакие новости не оторвут более чем на час.

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

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

    Уже сто раз описаны способы, как избавиться от новостной аддикции. Расширения для браузера, удаление телеграмов-ватсаппов с десктопа. Принцип “только с мобильника”, когда мобильник кладут на подоконник, и надо оторвать зад, чтобы до него дойти.

    Словом, не объясняйте слабую работоспособность “таким временем”. “Такое время” будет всегда: не ковид, так война, не война, так еще что.

    Если страсть побеждают внешние факторы, значит мало страсти. Если мало страсти, ее нужно найти.

  • Граждане выгорающие

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

    Как я выгорел и пережил это. Как я выгорел и пошел в автослесари. Как не выгореть тимлиду. Курсы для выгоревших. Исповедь выгоревшего менеджера.

    Сколько вас, нечисти, развелось.

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

    Стоит миру перевернуться, как уходит всякое выгорание. Из чего следует печальный факт. Массовое выгорание прошлых лет — пошлое ковыряние в себе на фоне скуки. Подобно организму, который в стерильных условиях борется с собой, человек в комфортных условиях ищет страдания.

    Все это было фарсом, позерством, пафосом.

    Повестка ушла, а задача ничуть не изменилась: все так же профессионально делать свое дело.

    За работу.

  • Возня с файлами

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

    Если коротко, с помощью крона я бекаплю файловый архив. До недавнего времени пользовался S3, но из-за последних событий вынужден переезжать. Ниже — мои изыскания на тему хранения файлов.

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

    Кроме фоток, крайне важны сканы документов. Паспорт, загран, снилс, ИНН и так далее. Все то же самое для жены и троих детей, плюс свидетельства о рождении. Полноценный скан документа (именно скан, а не фотка) неистово полезен. Распечатать его на принтере занимает минуту, а поход в ближайший копицентр — двадцать минут. Когда после рождения третьей дочки бегал по инстанциям, идею со сканами я просто боготворил: в каждую дверь нужно сдать пачку копий, а таких дверей десятки.

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

    До недавнего времени вопрос не стоял остро: бери любое облачное хранилище и вперед: дропбокс, яндекс-драйв, гугло-драйв, айклауд. Но даже до “спецоперации” и отвала русских пользователей ни один из этих сервисов мне не подошел. Все они намертво завязаны на конкретный бренд и любят выкручивать мозг. О деградации того же дропбокса я писал неоднократно (раз, два), и аналоги ничуть не лучше.

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

    Я попробовал AWS S3, и неожиданно понравилось: все происходит молча, разве что в раз в месяц получаю счет. Настроить просто: ставим питонячий AWS CLI. Далее прописываем креды и настройки в файлах:

    # ~/.aws/config
    
    [default]
    region = us-east-1
    output = json
    
    # ~/.aws/credentials
    
    [default]
    aws_access_key_id = ...
    aws_secret_access_key = ...
    

    Если теперь выполнить команду s3 sync:

    aws s3 sync my-bucket s3://my-bucket --delete
    

    , то произойдет синхронизация файлов из первого источника (папки) во второй (бакет). Флаг --delete означает, что файлы, которых нет в источнике, будут удалены в удаленном хранилище. Другими словами, если удалили локальный файл, то при синхронизации он будет удален и в S3. Без флага происходит простое слияние файлов.

    Вот как оформить все это дело в кронтаб:

    BUCKET=my-bucket
    
    0 */3 * * * cd /Users/ivan/s3 && aws s3 sync ${BUCKET} s3://${BUCKET} --exclude '*.DS_Store' --delete && echo OK
    

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

    Вопрос в том, насколько часто запускать синхронизацию. Раньше я делал это каждые три часа (0 */3 ...), но со временем понял, что это слишком часто. На практике хватает двух раз в день: в обед и вечером (0 12,22 ...).

    О результатах синхронизации я узнаю по письму следующего содержания:

    Subject: Cron <ivan@ivan> cd /Users/ivan/s3 && aws s3 sync ...
    
    Completed 60.5 KiB/~60.5 KiB (34.3 KiB/s) with ~1 file(s) remaining (calculating...)
    upload: my-bucket/docs/text/links.md to s3://my-bucket/docs/text/links.md
    Completed 60.5 KiB/~60.5 KiB (34.3 KiB/s) with ~0 file(s) remaining (calculating...)
    
    OK
    

    Про деньги: хранение 130 гигабайт с синхронизацией каждые 3 часа стоит 4.6 доллара в месяц. Это дешевле дропбокса и и аналогов с интерфейсом. Важное преимущество в том, что платишь в точности за те ресурсы, что потребляешь. Никаких обрыдлых подписок.

    С началом “не-войны” Амазон начал вести себя странно. Файлы по-прежнему доступны, но теперь я не могу войти в консоль управления. Амазон требует ввести код, отправленный на телефон, но сообщения не приходят. Не работают и другие способы его получить, например голосовым звонком робота. Запросил восстановление двухфакторной авторизации, но что-то Амазон не спешит.

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

    Приятная вещь: S3 уже давно не монополист в области хранения файлов, и многие фирмы выкатили свои решения. Которые, кстати, работают по протоколу S3. Файловые хранилища предлагают DigitalOcean, Exoscale, Яндекс.Облако. Будучи в Exoscale, я, хоть и немного, но работал над хранилищем.

    Миграция сводится к тому, чтобы перенацелить клиент AWS на другой сервис. По понятным причинам выбрал Яндекс — западные партнеры сейчас не подходят. Регистрируемся на cloud.yandex.ru, переходим в Object Storage. Создаем бакет. Советую холодный тип хранения, потому что читать файлы вы будете редко, а синхронизация работает по методу HEAD — через метаданные.

    Когда бакет создан, понадобится пара открытый-закрытый ключ. Как слепой щенок, долго я мыкался по интерфейсу, пока не нашел “сервисные аккаунты”. Создайте новую пару и сохраните ключи. Добавьте в настройки клиента AWS секции:

    # ~/.aws/config
    
    [ya]
    region = ru-central1
    output = json
    
    # ~/.aws/credentials
    
    [ya]
    aws_access_key_id = ...
    aws_secret_access_key = ...
    

    Теперь выполните:

    aws --profile ya --endpoint-url=https://storage.yandexcloud.net s3 sync ...
    

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

    Как закончится синхронизация, удалите старые файлы из Амазона:

    aws s3 rm s3://my-bucket --recursive
    

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

    У питонячьего AWS-клиента есть недочет: при синхронизации он оставляет на сервере пустые папки. Предположим, вы удалили папку “photos”, и AWS CLI честно выполнит DELETE для каждого файла:

    DELETE photos/IMG_001.jpeg
    DELETE photos/IMG_002.jpeg
    DELETE photos/IMG_003.jpeg
    ...
    

    Что касается пути “photos/”, то он благополучно останется на сервере. Ясное дело, что со временем накопится масса таких пустышек, и скачав файлы с S3, вы обнаружите папки, удаленные давным давно. На эту тему пять лет назад создан issue, в котором отметился и ваш покорный слуга. Каждый месяц я получаю письма с комментариями +1 и пальчиком кверху, но исправлять его никто не спешит.

    Впрочем, удалить пустые папки можно командой

    find . -type d -empty -delete
    

    , что не такая уж и проблема.

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

    Во время изысканий я в том числе пробовал программу SyncThing. Кто не знает, это программа для анонимной синхронизации файлов. Никаких регистраций: поставил у себя и на удаленной машине, обменялся QR-кодами, и процесс пошел. Работает как часы. Висит в трее, настройки через браузер на локальном хосте.

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

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

    В общем, вот как я синхронизирую файлы. Слово читателям: расскажите, как делаете это вы.

    UPD: продолжение.

  • Bogus: a simple GUI debugger for Clojure

    (This is a copy of the readme file from the repository.)

    Bogus is a small, GUI-powered, NIH-reasoned debugger for Clojure.

    Installation

    Lein:

    [com.github.igrishaev/bogus "0.1.0"]
    

    Deps.edn

    {com.github.igrishaev/bogus {:mvn/version "0.1.0"}}
    

    The best way to use Bogus is to setup it locally in your profiles.clj file:

    ;; ~/.lein/profiles.clj
    
    {:user
     {:dependencies [[com.github.igrishaev/bogus "0.1.0"]]
      :injections [(require 'bogus.core)]}}
    

    Usage

    Once you have the dependency added and the bogus.core namespace imported, place one of these two forms into your code:

    (bogus.core/debug)
    ;; or
    #bg/debug
    

    For example:

    (defn do-some-action []
      (let [a 1
            b 2
            c (+ a b)]
        #bg/debug ;; or (bogus.core/debug)
        (+ a b c)))
    

    Now run the function, and you’ll see the UI:

    The UI window blocks execution of the code. In the example above, you’ll hang right before executing the next (+ a b c) form. Close the window to continue execution of the code.

    The UI consists from three parts: the input area, the output, and the log. Type any Clojure-friendly form in the input textarea and press “Eval”. The result will take place in the output textarea. You can copy it from there to your editor.

    The input can take many Clojure forms at once. They are executed as follows:

    (eval '(do (form1) (form2) ...))
    

    so you’ll get the result of the last one.

    If you mark some text in the input with selection, only this fragment of code will be executed.

    In the input code, you can use any local variables as they’re global ones. In the example above we’ve executed the (+ a b c) form referencing local a, b, and c from the let clause.

    The “Locals” button pretty-prints the local variables. Bogus does it in advance when the window opens the first time. The “Inspect” button opens the standard clojure.inspector/inspect-tree widget rendering the locals. This is quite useful when examining massive chunks of data.

    The Log area tracks the history of the expressions you executed and their results. That’s useful sometimes to copy-paste it somewhere. Should you get an exception, it gets rendered with the clojure.stacktrace/print-stack-trace function (which probably needs some improvements).

    You can have several debug breakpoints, for example:

    (defn do-some-action []
      (let [a 1
            b 2
            c (+ a b)]
        #bg/debug
        (+ a b c)
        (let [d 9]
          #bg/debug
          (* a b c d))))
    

    The first debug session will take the a, b, and c locals, whereas the second one will have a, b, c, and d. You won’t proceed to the second session until you close the first window.

    Bogus debugger works in tests, in nREPL, in ordinary REPL, in Manifold, in the futures as well. Here is a small demo of having two debug sessions in parallel threads:

    (let [f1 (future
               (let [a 1]
                 #bg/debug
                 (+ a 1)))
          f2 (future
               (let [b 2]
                 #bg/debug
                 (+ b 1)))]
      (+ @f1 @f2))
    

    If you run this code, you’ll get the two windows each having their own locals:

    The second session has the f1 local var captured from the let clause. If you try to deref it, the entire REPL will hang due to the mutual blocking, so be careful when dealing with parallel debugging.

    How it works

    The under-hood of Bogus is simple: it captures the local vars from the &env mapping available in a macro. Then there is a couple of functions that “globalize” the locals by injecting them into the origin namespace using clojure.core/intern. Once the namespace is populated, the eval form treats local vars as globals. Before leaving the macros, the vars are “deglobalized” meaning all the injected vars are removed. The macros is smart enough to preserve existing global vars: it temporary assigns them another name like __OLD_my-var__.

    Why

    The idea of making my own debugger came into my mind while I was writing a new chapter about nREPL and code evaluation. Although Cider provides much more powerful tools for debugging, I still believe Bogus might be useful for someone new to Clojure. The main benefit of Bogus is, it doesn’t require the whole nREPL stuff and Emacs. One can use it with any editor or environment. After all, tinkering with Bogus gave me some good material for the book.

    Other

    Copyright © 2022 Ivan Grishaev

  • Увольнение

    Прошлый понедельник запомнился слитыми письмами, где фирмы разрывали контракты с российскими удаленьщиками. Вроде того, что от Гугла по ссылке. Тенденция не обошла меня стороной. Понедельник я провел в смутной тревоге, понимая, что работодатель способен на этот шаг. В пять вечера получил письмо об увольнении.

    По этическим причинам не будут постить сюда скрин — по моим меркам слив переписки находится ниже любого дна. Это был такой же абстрактный текст, как и у Гугла. Что-то в духе “мы ценим твою работу, ничего личного, прощай”.

    Дело в том, Иван, что как бы мы ни ценили три с половиной года твоей работы, есть некие полиси, которые предписывают порвать наши отношения. Что это за полиси, где их увидеть и прочесть – начальство не уточнило.

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

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

    Удивила скорость всей процедуры. В 17:00 я получаю персональное письмо. Через пятнадцать минут приходит анонс всей команде: Иван больше не с нами, будем скучать. Еще через десять минут отваливается доступ ко всей инфраструктуре. Никаких обсуждений, торгов: все линейно, от тебя ничего не зависит.

    Интересно, что адекватная реакция была только у одного человека – украинского коллеги. Он эмоционально ответил в тред с анонсом, что увольнение русских не поможет Украине и ваша гнилая солидарность никому не нужна. Но разве его кто-то слушал? Швейцария знает лучше Украины, как ей помочь.

    Начальство взялось отвечать на выпад: теперь мы не можем платить Ивану. Сухо возражу — никаких проблем с оплатой не было. МодульБанк, в котором я счастлив обслуживаться, не под санкциями, свифты доходят. Кроме свифта, в нем работает альтернативная система Visa B2B Connect, доступная и в Швейцарии. На какое-то время я бы согласился получать Пейпал или Transfer Wise. Можно попробовать крипту: биток, USD-токены. Тоже норм, есть знать как обращаться (а я знаю).

    Да что там: на худой конец я бы мог формально уволиться и поработать бесплатно, а потом, как все наладится, заключить контракт по новой ставке, чтобы компенсировать потери. За пять минут я назвал пять вариантов, а если подумать, найдутся другие. Но если сказано “не можем” — значит не можем.

    Украинский коллега, кстати, тоже попал в поле зрения “помогаторов” Украине. Если мой контракт расторгнут (агрессор), то его — “временно приостановлен” до тех пор, пока “ситуация не придет в норму” (жертва). Временно — это на сколько? Норма — как это понимать?

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

    Схожее происходит и в других фирмах: от русских надо избавиться, агрессоры, все дела. Но от украинцев тоже, только слегка по-другому. “Временно отключим” вас, пока ситуация “не придет в норму”.

    По идее я должен ругать Путина и его “спецоперацию”. Но дело в том, что уволил меня не Путин — это сделали конкретные люди. Которые, в свою очередь, вешают на меня груз: это не мы, это ваш президент, к нему и обращайтесь. Со мной это не работает. Я потерял работу потому, что менеджеры решили что-то доказать. Не сегодня Путин, так завтра нашелся бы другой предлог.

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

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

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

  • Если бы тебе сказали

    У демагогов популярен дешевый прием — если бы десять лет назад тебе сказали X, ты бы не поверил. Например:

    — если бы десять лет назад вам сказали, что Россия нападет на Украину, вы бы не поверили.

    — Если бы десять лет назад вам сказали, что мир сойдет с ума из-за ковида, вы бы не поверили.

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

    Не дайте вас обмануть. Мир непредсказуем и сложен, и никакой прогноз на десять лет не будет точным. Прогнозы не учитывают открытия, изобретения, психические отклонения политиков и прочие вещи, которые вокруг нас. Прогноз подразумевает, что за десять лет окружение не изменится, и за это время придет в то состояние, что показывает модель. А это неверно, неважно обычный у вас машын-лернинг или дип-машин-лернинг с подкреплением (Греф.jpeg).

    Возьмите любое событие и подставьте впереди “если бы десять лет назад”. Результат всегда будет истинным:

    — Если бы десять лет назад вам сказали, что биткоин будет стоить пять миллионов, вы бы не поверили.

    — Если бы десять лет назад вам сказали, что незаконно построенный павильон на остановке снесут, вы бы не поверили.

    — Если назвать человеку дату смерти за десять лет до нее, он бы не поверил.

    — За десять лет до Pokemon Go никто бы не поверил, что люди будут ловить виртуальных зверей на улице.

    — За десять лет до Чернобыля никто бы не поверил.

    — За десять лет до Титаника…

    — За десять лет до Чечни…

    — За десять лет до Гитлера…

    Короче, вы поняли — важные события предсказать невозможно. Отдельным личностям остается спекулировать: если случилось то, значит случится другое. Но это неверно.

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

    Меньше слушайте тех, кто говорит про прогнозы десятилетней давности.

  • Мысли об оппозиции

    Когда кто-то говорит об оппозиции в России, я удивляюсь. На мой взгляд, в России давно нет оппозиции. Есть партии и лица, которые называют себя оппозицией, и есть сочувствующие им СМИ, которые тоже их так называют. На реальность это никак не влияет: с таким же успехом я могу назвать себя хакером, потому что так думают в студии йоги за стеной.

    Особую роль в российской оппозиции сыграл Алексей Навальный. При всем сочувствии к нему как к жертве режима, я не считаю его ни политиком, ни оппозиционером. Всегда раздражался, когда читал сочувствующие ему сайты вроде Медузы, например:

    Алексей Навальный посетил Ростов. Вместе с волонтерами политик посетил… Оппозиционер высказался на тему … и подчеркнул, что…

    Ох, нет.

    Называть Навальным политиком неправильно потому, что мы обманываем себя: приписываем ложные черты и смешиваем понятия. Политик — и тем более, если он оппозиционер, — это человек, который борется за власть. Его цель в том, чтобы сбросить текущий режим и сесть сверху. Политиками и оппозиционерами были Ленин, Троцкий, Сталин, Зиновьев и прочие люди той эпохи. Нынешние лидеры “оппозиционных” партий в корне отличаются от них.

    Проблема современной оппозиции в том, что она не борется за власть. Если точнее, бесконечная борьба становится целью, смыслом ее существования. Наиболее яркие примеры — партии ЛДПР и КПРФ с их бессменными лидерами. Обе они давно превратились в семейный бизнес их основателей. Подобно сетевому маркетингу вроде Amway, от низов к верхам движутся огромные суммы денег. Крохи оседают на промежуточных уровнях, а львиная доля переходит членам семьи.

    И ЛДПР, и КПРФ бесконечно лояльны режиму, с которым они якобы борются. Изредка они могут позволить себе демарш вроде голосования против повышения пенсионного возраста. Но представить их голосующими против ввода войск в Украину невозможно. Ясно всем: сладкая жизнь лидеров быстро закончится.

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

    Разница в том, что Навальный моложе и потому действует агрессивней. Он умнее и использует современные технологии: Твиттер, Ютуб, Телеграм, мобильное приложение. Сути это не меняет: последние несколько лет Навальный не боролся за власть, а раскручивал медиа-проект имени себя. Это можно назвать журналистикой, блоггингом, но не политикой.

    Одно время Навальный увлекся теледебатами. Быстрый их обзор показывает, что его оппонентами никогда не были политики высшего ранга или крупные (читай — окологосударственные) бизнесмены. Что говорит об уровне Навального в иерархии. Оппоненты Навального — Собчак, Лебедев, Гоблин-Пучков и прочие — это представители среднего и малого бизнеса, которые дополнительно ведут медийную активность: пишут в ЖЖ или инстаграм. Отношения к политике у них нет никакого. С какой стати “политику” Навальному выходить с ними в эфир? Представить дебаты Навального с условным Лавровым или Грефом просто невозможно — слишком видна разница в весовой категории.

    Сходство медиа-машины Навального с КПРФ и ЛДПР уже в том, что это проекты одного человека. Может кто-нибудь из вас, не прибегая к Гуглу, назвать хотя бы пять сторонников их лидеров? Я помню только коммуниста Грудинина, который по неосторожности выиграл выборы в какой-то области. По неосторожности — потому что все, включая коммунистов, засуетились. По итогам “переговоров” Грудинин, как и положено “оппозиции”, согласился на пересмотр итогов с заведомо известным результатом. Скорее всего, не в ущерб своим материальным интересам.

    Когда мы говорим о классических революционерах, они никогда не упоминаются в одиночку. Возьмите школьный учебник за восьмой класс: семнадцатый год, революция. Ленин никогда не упоминается один. Эпоху революции всегда проходят, изучая Троцкого, Сталина и прочих. Да, в конце они замочили друг друга, но это в конце. Действие велось многими людьми и по разным направлениям. Ничего подобно сегодня нет.

    Что касается Навального, то из его структуры я назову только Волкова, и то потому, что он запомнился мне лютейшим скандалом. Сравнительно недавно у ФБК увели личные данные пользователей, собранных в рамках “умного” голосования (в кавычках, потому что ничего умного там нет). В принципе, не такая уж и трагедия: Сбер, Тиньков и Билайн сливались не раз, но проехали. Мог бы проехать и ФБК, тем более, что пользователи были к ним лояльны, если бы не дискуссия с Волковым в интернете.

    Сначала в ФБК не признавали слива; затем признали, но переложили вину на хостера, почтовую рассылку и бог знает кого еще. Затем пластинка опять сменилась: персональные данные были не такими уж и персональными, поэтому что вы разнылись. Версии менялись каждый час. Волков потерял лицо: айтишников, которые нашли уязвимости в ФБК, назвал агентами Кремля и путиноидами. Все это было ужасно, хотя, повторюсь, изначально аудитория была настроена с пониманием.

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

    Ни разу (ну или, может, недостаточно громко) Навальный не озвучил то, что действительно нужно сделать в России прямо сейчас.

    • Немедленно выйти из всех войн. На Украине и Сирии, неважно на каких условиях. Не через год, а завтра. То же самое с частными наемниками в Ливии и других странах Африки. За все эти упражнения платит честный гражданин.

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

    • Разогнать Росгвардию, суть которой сводится к избиению людей на митингах. Сократить прочих силовиков, полчища которых сосут бюджет почем зря. Массово сменить судей, ментов, чиновников.

    • Самое главное — разработать проект будущего России на ближайшие 30-50 лет. Предложить новую идеологию, ценности. Зачем мы живем в России, к чему стремится, что строим?

    Вот что я бы хотел услышать от Навального и его людей. А не про дачу Медведева, дворец Путина или чью-то виллу в Греции. Навальный так и не понял запросов взрослых людей. Он ориентировался на молодежь, живущую в интернете. А молодежь несамостоятельна — им помогают родители, они не платят коммуналку, не замечают ухудшения продуктов после “нашего Крыма”. Все это проходит мимо них. Молодежь хочет нового ролика про вора-чиновника, чтобы сделать репост и обсудить в курилке.

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

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

    Как бы то ни было, желаю Алексею сохранить здоровье в тюрьме и поскорее выйти оттуда. Таковы мои мысли о русской оппозиции. Не берите близко к сердцу, если не совпадают с вашими.

  • Единомышленники (+18)

    Давно не читал Торшину и вдруг. Скриню для истории, на Фейсбук полагаться нельзя.

  • The Pact library for Clojure

    (This is a copy of the readme file from the repository.)

    Pact is a small library for chaining values through forms. It’s like a promise but much simpler.

    Installation

    Lein:

    [com.github.igrishaev/pact "0.1.0"]
    

    Deps.edn

    {com.github.igrishaev/pact {:mvn/version "0.1.0"}}
    

    How it works

    The library declares two universe handlers: then and error. When you apply them to the “good” values, you propagate further. Applying the error for them does nothing. And vice versa: then for the “bad” values does nothing, but calling error on “bad” values gives you a chance to recover the pipeline.

    By default, there is only one “bad” value which is an instance of Throwable. Other types are considered positive ones. The library carries extensions for such async data types as CompletableFuture, Manifold and core.async. You only need to require their modules so they extend the IPact protocol.

    Examples

    Import then and error macros, then chain a value with the standard -> threading macro. Both then and error accept a binding vector and an arbitrary body.

    (ns foobar
      (:require
       [pact.core :refer [then error]]))
    
    
    (-> 42
        (then [x]
          (-> x int str))
        (then [x]
          (str x "/hello")))
    
    "42/hello"
    

    If any exception pops up, the sequence of then handlers gets interrupted, and the error handler gets into play:

    (-> 1
        (then [x]
          (/ x 0))
        (then [x]
          (str x "/hello")) ;; won't be executed
        (error [e]
          (ex-message e)))
    
    "Divide by zero"
    

    The error handler gives you a chance to recover from the exception. If you return a non-exceptional data in error, the execution will proceed from the next then handler:

    (-> 1
        (then [x]
          (/ x 0))
        (error [e]
          (ex-message e))
        (then [message]
          (log/info message)))
    
    ;; nil
    

    The -> macro can be nested. This is useful to capture the context for a possible exception:

    (-> 1
        (then [x]
          (+ x 1))
        (then [x]
          (-> x
              (then [x]
                (/ x 0))
              (error [e]
                (println "The x was" x)
                nil))))
    
    ;; The x was 2
    ;; nil
    

    Besides then and error macros, the library provides the then-fn and error-fn functions. They are useful when you have a ready function that processes the value:

    (ns foobar
      (:require
       [pact.core :refer [then-fn error-fn]]))
    
    (-> 1
        (then-fn inc)
        (then-fn str))
    
    ;; "2"
    
    (-> 1
        (then [x]
          (/ x 0))
        (error-fn ex-message))
    
    ;; "Divide by zero"
    

    Chaining with then and error is especially good for maps as allowing destructuring:

    (-> {:db {...} :cassandra {...}}
    
        ;; Get a user from the database and attach it to the scope.
        (then [{:as scope :keys [db]}]
          (let [user (jdbc/get-by-id db :users 42)]
            (assoc scope :user user)))
    
        ;; Having a user, get their last items from Cassandra cluster
        ;; and attach them to the scope.
        (then [{:as scope :keys [cassandra user]}]
          (let [items (get-user-items cassandra user)]
            (assoc scope :items items)))
    
        ;; Do something more...
        (then [...]
          ...))
    

    Fast fail

    To interrupt the chain of then handlers, either throw an exception or use the failure function which is just a shortcut for raising a exception. The function takes a map or a message with a map:

    (ns foobar
      (:require
       [pact.core :refer [then error failure]]))
    
    (-> 1
        (then [x]
          (if (not= x 42)
            (failure "It was not 42!" {:x x})
            (+ 1 x)))
        (error-fn ex-data))
    
    ;; {:x 1 :ex/type :pact.core/failure}
    

    Supported types

    The core namespace declares the then and error handlers for the Object, Throwable, and java.util.concurrent.Future types. The Future values get dereferenced when passing to then.

    The following modules extend the IPact protocol for asynchronous types.

    Completable Future

    The module pact.comp-future handles the CompletableFuture class available since Java 11. The module also provides its own future macro to build an instance of CompletableFuture:

    (-> (future/future 1)
        (then [x]
          (inc x))
        (then [x]
          (/ 0 0))
        (error [e]
          (ex-message e))
        (deref))
    
    "Divide by zero"
    

    Pay attention: if you fed an instance of CompletableFuture to the threading macro, the result will always be of this type. Thus, there is a deref call at the end.

    Infernally, the then handler calls for the .thenApply method if a future and the error handler boils down to .exceptionally.

    Manifold

    The pact.manifold module makes the handlers work with the amazing Manifold library and its types. The Pact library doesn’t have Manifold dependency: you’ve got to add it on your own.

    [manifold "0.1.9-alpha3"]
    
    (-> (d/future 1)
        (then [x]
          (/ x 0))
        (error [e]
          (ex-message e))
        (deref))
    
    "Divide by zero"
    

    Under the hood, then and error handlers call the d/chain and d/catch macros respectively.

    Once you’ve put an instance of Manifold deferred, the result will always be a Deferred.

    Core.async

    To make the library work with core.async channels, import the pact.core-async module:

    (ns foobar
      (:require
       [pact.core :refer [then error]]
       [pact.core-async]
       [clojure.core.async :as a]))
    

    Like Manifold, the core.async dependency should be added by you as well:

    [org.clojure/core.async "1.5.648"]
    

    Now you can chain channels through the then and error actions. Internally, each handler takes exactly one value from a source channel and returns a new channel with the result. For then, exceptions traverse the channels being untouched. And instead, the error handler ignores ordinary values and affects only exceptions. Quick demo:

    (let [in (a/chan)
          out (-> in
                  (then [x]
                    (/ x 0))
                  (error [e]
                    (ex-message e))
                  (then [message]
                    (str "<<< " message " >>>")))]
    
      (a/put! in 1)
    
      (a/<!! out) )
    
    ;; "<<< class java.lang.String cannot be cast ..."
    

    Testing

    To run the tests, do lein test or just make test.

    © 2022 Ivan Grishaev

Страница 32 из 87