• Новый макбук

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

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

    Новый мак интересен тем, что он кастомный: у него экран с нанотекстурой. Дело в том, что мне нравятся матовые экраны. Дело вкуса, и я за это не топлю, просто нравится. Кто-то гонится за герцовкой, кто-то за матрицей, а мне нужен матовый экран.

    Мой первый ноут Thinkpad x220i потому и покорил мое сердце, что был матовым. Я искренне не понимаю, как можно смотреть в экран, который на две трети отражает все вокруг. Я хочу экран, а не зеркало.

    Конечно, везде написано “антибликовое покрытие”, но это ерунда — оно бликует и отражает.

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

    Что это за нанотекстура такая? Прежде чем ответить, вспомним, как устроен матовый экран. Это обычный глянцевый экран, на который наклеена шершавая пленка. Из-за неровности она рассеивает свет, и получается матовый эффект. Пленка немного скрадывает яркость и угол обзора; ее можно отодрать и получить обычный глянцевый экран.

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

    Я как-то видел 6К-монитор с нанотекстурой в московском ЦУМе. Продавец даже позволил подключить к нему макбук! Но ничего особого я не заметил: обычный матовый экран. За что платить такие деньги?

    Я думал, что эксперименты Эпла с текстурой на этом закончатся, но нет. Постепенно ее добавили в другие устройства, например iMac (дополнительной опцией). И вот недавно выкатили в айпадах и макбуках. Устройства с текстурой относятся к категории “кастомных”: их собирают не массово, а на заказ, из-за чего вы не найдете их в розничной продаже. У “кастомок” свои заморочки, о которых я напишу ниже, а пока что вернется к экрану.

    Итак, матовый экран — это сказка. Он ничего не отражает, он мягкий, щадит глаза. Светишь на него фонариком — почти все рассеивается. Если сфотографировать экран, вы увидите его содержимое, а не селфи с телефоном. Не заметил, чтобы яркость, угол или рендер шрифтов как-то страдали. Все отлично читается.

    Видел картинки шрифтов, сделанные микроскопом с разных экранов. На матовом четкость букв чуть меньше, да, но обычному глазу это незаметно.

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

    Архитектура ARM — это, конечно, полная победа Эпла. После Интела ощущение такое, что спали невидимые гири — настолько все быстро. Ноут больше не закипает при запуске Докера. Программы выскакивают почти мгновенно. Батарея живет ощутимо дольше, раза в два точно.

    У меня уже была машина на M1, и даже она после Интела казалась прорывом. А чипы M3-M4 просто творят чудеса. Самое важное: подсознательно чувствуется, что это не ARM в разы быстрее, а Интел что-то делал не так. Иначе откуда такой буст в производительности?

    С софтом ситуация не такая радостная. Насколько я понял, всех толковых разрабов забрали в айфон, и десктоп пилят по остаточному принципу. Накопил десятки скринов со всякими нелепостями.

    Если вы все еще на Интеле, горячо советую перейти на чипы M1-M4. Это буквально вложить деньги в себя, в свою продуктивность.

    Теперь о том, что такое “кастомки”. Есть стандартные конфигурации макбуков, которые массово клепают на заводах Китая и Индии. Именно их вы видите в магазинах. А есть частные конфигурации, доступные на сайте Эпла. Такие модели собирают в США, и это сказывается на доставке: долго и дорого. Кастомки ввозят в соотношении 1 к 10 относительно стандартных конфигураций, потому что на них меньше покупателей. Вероятность того, что у продавца найдется именно та кастомка, которую хочется вам, низкая.

    Немного о грустном. Не все так хорошо, кое-где я просчитался, а именно в клавиатуре. Есть два широких стандарта клавиш: европейский и США. У Европы следующие особенности:

    • кнопка Enter вертикальная
    • обратный слэш слева от него
    • тильда и обратный тик слева от левого шифта

    У раскладки США все не так:

    • Enter горизонтальный
    • обратный слэш сверху
    • тильда и обратный тик слева от единицы

    Много лет я сидел на европейском стандарте: как на самом ноуте, так и на внешней клавиатуре. А поскольку кастомки собирают в США, то положение клавиш у них тоже по стандарту США. И насколько я знаю, на это нельзя повлиять. Заказал кастомку — получи раскладку США. В конфигураторе можно выбрать дополнительный язык клавиш, но это всего лишь гравировка. Макет клавиатуры не поменяется.

    Получаются боль и слезы. В сотый раз нажимаю Enter и попадаю в обратный слэш. Жму тильду – а там растянутый шифт. Только сейчас понял, насколько важна тильда: она часто встречается в кложурных макросах. Она нужна в маркдауне, а маркдаун нынче везде: в Телеграме, в Слаке, в Гитхабе и так далее.

    Ясно, что когда-нибудь переучусь, но подгорает порой знатно.

    Вот что я хотел рассказать вам про маки и Эплы.

  • Bathroom model

    Интересное чтиво на выходные. Некие чуваки написали пейпер о новом методе поиска свободных ячеек в хеш-таблице (по-другому называется пробирование). Метод называется Bathroom model и является отсылкой к тому, как ищется пустая кабинка в общественном туалете. Не припомню, когда в последний раз искал кабинку по этой схеме, но спорить не буду. Прилагаются сравнение с тремя другими способами: случайным, по экспоненте и воронкой.

    В некоторых случаях Bathroom быстрее конкурентов, но. Код на гитхабе довольно скромный, и хотелось бы больше замеров. На мой взгляд, говорить о прорыве рано, но повозиться интересно. Код и замеры авторов.

    UPD: на проверку оказалось ерундой.

  • О еврочиновниках

    Я как-то писал о проблеме, что каждый сайт показывает выпадашку с куками. В конце обещал рассказать о еврочиновниках, которые все это устроили. Эта заметка – о них.

    В широком смысле еврочиновник не отличается от российского или американского чиновника. Их главная черта – реактивный стиль управления. “Реактивный” означает не “быстрый”, а от слова “реакция”. Сперва чиновник реагирует самым глупым образом, а потом (и если вообще) думает, нужно ли было реагировать.

    Пример: если с крыши падает лед, поставим таблички “осторожно, падает лед”. Если кто-то следит за пользователем при помощи кук, поставим плашку “этот сайт использует куки”. В обоих случаях главное – выполнить KPI, отчитаться и пойти дальше. О последствиях чиновник не думает, ему не до таких мелочей.

    В результате 99 сайтов из 100 показывают плашки, что сайт исползует куки. Уже одно оформление говорит о полной неразберихе. На одном сайте это полоска внизу экрана, а на другом – модалка во весь экран. Где-то одно предложение, где-то графомания, которая не вмещается по высоте. Кто-то блюрит контент, пока не нажмешь кнопку.

    На фоне неразберихи появляются сервисы, которые берут часть проблем на себя. Например, Cookiehub внедряет виджет выбора кук. Их клиентов можно понять: проще платить двадцать евро в месяц, чем попасть по доносу конкурентов на 30 тысяч евро (реальный случай).

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

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

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

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

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

    Искренне не понимаю, почему в Европе не нашлось энтузиастов, которые положили бы этому конец? Я имею в виду группу экспертов: программистов, опенсорщиков, членов всяких комитетов. Они бы собрали материалы и факты, организовали бы встречу с чиновниками и популярно объяснили им, что они натворили. Добились бы частичной отмены и точных формулировок. Но ничего подобного не видно: все послушно лепят плашки на сайтах, словно овцы, которых ведут на убой. Всякие Греты Тунберг и другие отбитые личности собирают толпы, когда речь о борьбе с климатом и ковиде. А когда что-то важное, то тишина: это для нашей безопасности.

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

  • Аналоговое образование

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

    Теперь длинно. Российское айти развивается довольно неплохо, и верный признак этого – компании собирают деньги там, где раньше не могли, а именно в образовательном секторе. Исторически считалось, что школы бедные и с них нечего взять. Однако оплату повесили на родителей, и дело пошло. Напоминает фирмы, которые продают товары детям – аудитории, у которой нет денег – но за них платят родители.

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

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

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

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

    Учителя и школы оказались голыми. Нет никакой базы знаний: офлайн-лекций, методичек, контрольных работ, которые распечатал и готово. Я как отец трех детей (двое учатся в школе) просто ох…ал с этого, простите. Когда училка опять кидает ссылку на образовательный портал, хотелось лупить ее линейкой со словами: ты учитель или кто? Сядь и составь задание для детей, это же час работы. Тебе же самой пригодится в дальнейшем. Какого хрена ты тащишь детей в очередной стартап?

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

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

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

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

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

  • Что такое Disruptor?

    Графомания бывает не только в русском, но и в английском языке. Хороший пример — страница проекта Disruptor.

    Вижу большой заголовок “What is Disruptor?”, но ни один параграф не отвечает на вопрос, что это такое.

    Первый параграф о том, что в такой-то фирме борются за перформанс.

    Второй параграф о том, что Disruptor — это результат исследований и тестов. Мы тестировали кеши процессора и ядра и сделали дружественный к железу фреймворк.

    Третий параграф — это не специализированное решение, подойдет всем.

    Четвертый параграф — он работает не так как вы привыкли, может быть непривычно.

    Далее ссылки и документация, а еще нас залайкал известный чел.

    Внимание, вопрос — что такое Disruptor?

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

    Не надо так. Отвечайте на заголовок, который сами же написали.

  • Вырезалки на Маке

    У меня тут накладки: вышел из строя личный ноут. Ставить Телеграм и прочие штучки на рабочий ноут я не рискую, поэтому пару дней потратил вот на что: достал с антресолей стренький мак 2014 года. Мой первый мак, в котором, как в греческом корабле, давно заменены все детали. Но я считаю его тем самым маком.

    Переустановил ось, накатил софт, и в процессе пришла в голову эта заметка.

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

    В один момент я всего этого лишился. Ощущения ужасные.

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

    Про то, что каждое приложение лезет в сеть за обновлениями и показывает модалку, я писал много раз. Приходиться ставить Lulu и затыкать софт.

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

    Сюда же Телеграм: если в системе английский язык, предлагает перевести русские каналы. Если русский, то стоит кому-то скопипастить английскую Википедию, как сразу выпадашка с переводом.

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

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

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

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

    Вырезаю все это по-новой и думаю: что будет через десять лет?

  • Схемы JSON

    Официальный сайт json.org удивляет. В масштабе 30% схемы становятся огромны и заливают весь экран. В масштабе 300% правый бар наезжает на основную часть, сжимает ее в мышиный хвост, отчего схемы тоже уменьшаются.

    В результате схема в масштабе 30% процентов выглядит крупнее, чем в 300%. Разница, на минуточку, целый порядок. Такой вот забавный факт.

    У этих выкрутасов есть объяснения: верстка, плавающие дивы, стили, пятое-десятое. Но можно сказать проще: это фронтенд.

  • Ответ с эмодзи

    Важная новость: в Microsoft Outlook появились реакции к емейлам. Работает так: вы написали письмо, отправили, а под ним появляются пальчики, сердечки и прочее. Пошарить скриншот не могу, поверьте на слово.

    Интересно, как это работает? Особая апишка в протоколе Outlook? Или пустое письмо с заголовком? Или какой-то особый пейлоад? Или если в письме только эмодзи, оно показывается по-другому?

    В любом случае, шлю лучи добра пользователям Аутлука.

  • Корпоративные обновления

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

    Так вот, в который раз попадаюсь на дурацкое поведение Эпла. Вылазит выпадашка: обновись до Секвойи, тянуть больше нельзя. И кнопочка “Install update”. Я закрываю все программы, все реплы, докеры и остаюсь с выпадашкой наедине. Жму кнопку и вижу: отлично, загружаю обновление, осталось 3 часа. Три часа, Карл! Я мог бы ничего не закрывать и работать чуть ли не полдня, пока качается обновление!

    Нажимая “Install update”, я рассчитываю, что оно уже загружено и я в шаге от того, чтобы начать установку. А загрузка даже не начиналась! И качать там не три мегабайта, а 5-7 гигов.

    Спрашивается, что мешало скачать обновление в фоне? Если от него нельзя отделаться, так скачай сам, зачем парить мне мозги?

    Дизайнер тоже хорош: если кнопка запускает загрузку обновления, ее нужно назвать “Download & Install update”. Я ведь хочу самую малость: чтобы на кнопке было написано то, что она делает на самом деле.

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

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

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

  • Taggie

    Taggie is an experimental library trying find an answer for a strange question: is it possible to benefit from Clojure tags and readers, and how?

    Taggie extends printing methods such that types that could not be read from their representation now can be read. A quick example: if you print an atom, you’ll get a weird string:

    (atom 42)
    #<Atom@7fea5978: 42>
    

    Run that string, and REPL won’t understand you:

    #<Atom@7fea5978: 42>
    Syntax error reading source at (REPL:962:5).
    Unreadable form
    

    But with Taggie, it goes this way:

    (atom 42)
    #atom 42 ;; represented with a tag
    

    And vice versa:

    #atom 42 ;; run it in repl
    #atom 42 ;; the result
    

    The value is an atom indeed, you can check it:

    (deref #atom 42)
    42
    

    Tags can be nested. Let’s try some madness:

    (def omg #atom #atom #atom #atom #atom #atom 42)
    
    (println omg)
    #atom #atom #atom #atom #atom #atom 42
    
    @@@@@@omg
    42
    

    But this is not only about atoms! Taggie extends many types, e.g. refs, native Java arrays, File, URI, URL, Date, java.time.* classes, and something else. See the corresponding section below.

    Installation and Usage

    Add this to your project:

    ;; lein
    [com.github.igrishaev/taggie "0.1.0"]
    
    ;; deps
    com.github.igrishaev/taggie {:mvn/version "0.1.0"}
    

    Then import the core namespace:

    (ns com.acme.server
      (:require
        taggie.core))
    

    Now type in the repl any of these:

    #LocalDate "2025-01-01"
    #Instant "2025-01-01T23:59:59Z"
    #File "/path/to/a/file.txt"
    #URL "https://clojure.org"
    #bytes [0x00 0xff]
    #ints [1 2 3]
    #floats [1 2 3]
    #ByteBuffer [0 1 2 3 4]
    ...
    

    Each expression gives an instance of a corresponding type: a LocalDate, an Instane, a File, etc… #bytes, #ints and similar produce native Java arrays.

    You can pass tagged values into functions as usual:

    (deref #atom 42)
    42
    
    (alength #longs [1 2 3])
    3
    

    To observe what happends under the hood, prepend your expression with a backtick:

    `(alength #longs [1 2 3])
    
    (clojure.core/alength (taggie.readers/__reader-longs-edn [1 2 3]))
    

    Internally, all tags expand into an invocation of an EDN reader. Namely, #longs items becomes (taggie.readers/__reader-longs-edn items), and when evaluated, it returs a native array of longs.

    EDN Support

    Taggie provides functions to read and write EDN with tags. They live in the taggie.edn namespace. Use it as follows:

    (def edn-dump
      (taggie.edn/write-string #atom {:test 1
                                      :values #longs [1 2 3]
                                      :created-at #LocalDate "2025-01-01"}))
    
    (println edn-dump)
    
    ;; #atom {:test 1,
    ;;        :values #longs [1, 2, 3],
    ;;        :created-at #LocalDate "2025-01-01"}
    

    It produces a string with custom tags and data being pretty printed. Let’s read it back:

    (taggie.edn/read-string edn-dump)
    
    #atom {:test 1,
           :values #longs [1, 2, 3],
           :created-at #LocalDate "2025-01-01"}
    

    The write function writes EDN into a destination which might be a file path, a file, an output stream, a writer, etc:

    (taggie.edn/write (clojure.java.io/file "data.edn")
                      {:test (atom (ref (atom :secret)))})
    

    The read function reads EDN from any kind of source: a file path, a file, in input stream, a reader, etc. Internally, a source is transformed into the PushbackReader instance:

    (taggie.edn/read (clojure.java.io/file "data.edn"))
    
    {:test #atom #ref #atom :secret}
    

    Both read and read-string accept standard clojure.edn/read options, e.g. :readers, :eof, etc. The :readers map gets merged with a global map of custom tags.

    Motivation

    Aside from jokes, this library might save your day. I often see people dump data into .edn files, and the data has atoms, regular expressions, exceptions, and other unreadable types:

    (spit "data.edn"
          (with-out-str
            (clojure.pprint/pprint
              {:regex #"foobar"
               :atom (atom 42)
               :error (ex-info "boom" {:test 1})})))
    
    (println (slurp "data.edn"))
    
    {:regex #"foobar", :atom #<Atom@4f7aa8aa: 42>, :error #error {
     :cause "boom"
     :data {:test 1}
     :via
     [{:type clojure.lang.ExceptionInfo
       :message "boom"
       :data {:test 1}
       :at [user$eval43373$fn__43374 invoke "form-init6283045849674730121.clj" 2248]}]
     :trace
     [[user$eval43373$fn__43374 invoke "form-init6283045849674730121.clj" 2248]
      [user$eval43373 invokeStatic "form-init6283045849674730121.clj" 2244]
      ;; truncated
      [clojure.lang.AFn run "AFn.java" 22]
      [java.lang.Thread run "Thread.java" 833]]}}
    

    This dump cannot be read back due to:

    1. unknown #"foobar" tag (EDN doesn’t support regex);
    2. broken #<Atom@4f7aa8aa: 42> expression;
    3. unknown #error tag.

    But with Taggie, the same data produces tagged fields that can be read back.

    Supported Types

    In alphabetic order:

    Type Example
    java.nio.ByteBuffer #ByteBuffer [0 1 2]
    java.util.Date #Date "2025-01-06T14:03:23.819Z"
    java.time.Duration #Duration "PT72H"
    java.io.File #File "/path/to/file.txt"
    java.time.Instant #Instant "2025-01-06T14:03:23.819994Z"
    java.time.LocalDate #LocalDate "2034-01-30"
    java.time.LocalDateTime #LocalDateTime "2025-01-08T11:08:13.232516"
    java.time.LocalTime #LocalTime "20:30:56.928424"
    java.time.MonthDay #MonthDay "--02-07"
    java.time.OffsetDateTime #OffsetDateTime "2025-02-07T20:31:22.513785+04:00"
    java.time.OffsetTime #OffsetTime "20:31:39.516036+03:00"
    java.time.Period #Period "P1Y2M3D"
    java.net.URI #URI "foobar://test.com/path?foo=1"
    java.net.URL #URL "https://clojure.org"
    java.time.Year #Year "2025"
    java.time.YearMonth #YearMonth "2025-02"
    java.time.ZoneId #ZoneId "Europe/Paris"
    java.time.ZoneOffset #ZoneOffset "-08:00"
    java.time.ZonedDateTime #ZonedDateTime "2025-02-07T20:32:33.309294+01:00[Europe/Paris]"
    clojure.lang.Atom #atom {:inner 'state}
    boolean[] #booleans [true false]
    byte[] #bytes [1 2 3]
    char[] #chars [\a \b \c]
    double[] #doubles [1.1 2.2 3.3]
    Throwable->map #error <result of Throwable->map> (see below)
    float[] #floats [1.1 2.2 3.3]
    int[] #ints [1 2 3]
    long[] #longs [1 2 3]
    Object[] #objects ["test" :foo 42 #atom false]
    clojure.lang.Ref #ref {:test true}
    java.util.regex.Pattern #regex "vesion: \d+"
    java.sql.Timestamp #sql/Timestamp "2025-01-06T14:03:23.819Z"

    The #error tag is a bit special: it returns a value with no parsing. It prevents an error when reading the result of printing of an exception:

    (println (ex-info "boom" {:test 123}))
    
    #error {
     :cause boom
     :data {:test 123}
     :via
     [{:type clojure.lang.ExceptionInfo
       :message boom
       :data {:test 123}
       :at [taggie.edn$eval9263 invokeStatic form-init2367470449524935680.clj 97]}]
     :trace
     [[taggie.edn$eval9263 invokeStatic form-init2367470449524935680.clj 97]
      [taggie.edn$eval9263 invoke form-init2367470449524935680.clj 97]
      ;; truncated
      [java.lang.Thread run Thread.java 833]]}
    

    When reading such data from EDN with Taggie, you’ll get a regular map.

    Adding Your Types

    Imagine you have a custom type and you want Taggie to hande it:

    (deftype SomeType [a b c])
    
    (def some-type
      (new SomeType (atom :test)
                    (LocalDate/parse "2023-01-03")
                    (long-array [1 2 3])))
    

    To override the way it gets printed, run the defprint macro:

    (taggie.print/defprint SomeType ^SomeType some-type writer
      (let [a (.-a some-type)
            b (.-b some-type)
            c (.-c some-type)]
        (.write writer "#SomeType ")
        (print-method [a b c] writer)))
    

    The first argument is a symbol bound to a class. The second is a symbol bound to the instance of this class (in some cases you’ll need a type hint). The third symbol is bound to the Writer instance. Inside the macro, you .write certain values into the writer. Avobe, we write the leading "#SomeType " string, and a vector of fields a, b and c. Calling print-method guarantees that all nested data will be written with their custom tags.

    Now if you print some-type or dump it into EDN, you’ll get:

    #SomeType [#atom :test #LocalDate "2023-01-03" #longs [1 2 3]]
    

    The opposite step: define readers for SomeType class:

    (taggie.readers/defreader SomeType [vect]
      (let [[a b c] vect]
        (new SomeType a b c)))
    

    It’s quite simple: the vector of fields is already parsed, so you only need to split it and pass fields into the constructor.

    The defreader mutates a global map of EDN readers. When you read an EDN string, the SomeType will be held. But it won’t work in REPL: for example, running #SomeType [...] in REPL will throw an error. The thing is, REPL readers cannot be overriden in runtime.

    But you can declare your own readers: in src directory, create a file called data_readers.clj with a map:

    {SomeType some.namespace/__reader-SomeType-clj}
    

    Restart the REPL, and now the tag will be available.

    As you might have guessed, the defreader macro creates two functions:

    • __reader-<tag>-clj for a REPL reader;
    • __reader-<tag>-edn for an EDN reader.

    Each -clj reader relies on a corresponding -edn reader internally.

    Emacs & Cider caveat: I noticed that M-x cider-ns-refresh command ruins loading REPL tags. After this command being run, any attempt to execute something like #LocalDate "..." ends up with an error saying “unbound function”. Thus, if you use Emacs and Cider, avoid this command.

Страница 20 из 109