brain_leakage_etc
119 subscribers
17 photos
2 videos
31 links
Не мысли, но мыслишки некоего @astynax. Можно сказать то, что не получилось дописать до средней длины публикаций в @brain_dump_etc
Download Telegram
Скатался на чайные плантации в Ткибули. Собрал чай, переработал. Теперь у меня есть пачка зелёного чая, во всех этапах производства которого я участвовал. На пачке фотка с нашей группой, все дела — там на плантации термопринтер для такого дела имеется, прям на тех же лентах печатает, которые и мой чековый фотик использует.

Теперь надо бы ещё до плантации кофе добраться, а то в кофе я гораздо более давнее (🌚), чем в чае.
🔥10
Сегодня у меня интересный день был. Попердолился с Haskell. Узнал много нового (о себе в том числе).

Мне понадобилось поставить одну тулзу — haskell-ci — в рамках старого проекта на Haskell, потому что оказалось, что там сломан GitHub workflow, который был сгенерирован этой самой тулзой. И даже добавлял этот workflow не я, мне оный наконтрибутили (установку haskell-ci не прописали и даже в README не упомянули) 🌚 Вощм, я научился ставить утилиты только для конкретного Cabal-проекта. По дороге пришлось понизить версию компилятора, потому что haskell-ci не умеет в свежий GHC 9.10.1🙈 — буквально не хочет воспринимать значение в конфиге проекта. Ок, разрешил на этой версии не тестировать пока.

Когда я с вопросом пошёл в Haskell чат, мне популярно рассказали, что "надо было ставить Nix, пёс!". Классика 🌚 Отбился, пока посижу на brew. GHCUp всё же пришлось поставить, чтобы подтянуть версию GHС постарее. Заодно узнал, что ghcup (кажется) не умеет в рамках проекта выбирать версию компилятора. А sdkman и asdf умели. Ну ок, в этот раз не помешало, просто не стал в PATH прописывать путь до ~/.ghcup безусловно и заменил на функцию, которая в конкретном shell окружение будет патчить — так я смогу использовать GHC от brew большую часть времени.

Обновление того workflow не помогло, кстати. Дело в том, что haskell-ci использует базовый образ buildpack-deps с довольно-таки старым тегом ("bionic"). Мой обновлённый workflow сломался на том, что Node 20 упала — я хаскельный проект собираю, если что 🤡. Помог бамп тега базового образа — вручную, потому что в коде всё ещё нет Bullseye (зато type safe enum!).

Вощм, проблема решена. Установка Nix откладывается. Погружаться в GH CI особо не пришлось и отрубать его совсем не понадобилось. Удачный день, чё!
🤯6
Хехе, ко мне в линкедин (!) пришёл человек и предъявил за моё ревью книги "An Outsider’s Guide to Statically Typed Functional Programming" на Goodreads!

Книжку я читал ещё в 2020. Тогда были написаны части про Elm и PureScript, материал про Idris не написан и по сей день. Книга ещё тогда мне показалась ну очень странной. Даже если учесть, что писатель сам себя преподносит как человека со стороны (отсюда и название книги). Автор притащил в Elm линзы, а состоянием управлял посредством машины состояний, реализованной на продолжениях — вот эту самую неконвенциональность я в ревью и отметил. Не ожидал, что кто-то это самое ревью прочитает, а потом ещё и с вопросом придёт 🙈

Вот такой вопрос я получил вчера:

Hi Aleksei, I spotted your Goodreads review for https://leanpub.com/outsidefp and am currently reading the book. I wondered what you meant by “too far from the common styles” … are you saying the way he’s coding is not the accepted way of doing things? I’m also reading “Functional Programming Made Easier” which is PureScript, but I’m mostly interested in Elm Lang. It’s compiler errors are far more helpful.


Пришлось сходить за свежей версией книги и посмотреть, что изменилось. После этого я на своём ломаном ответил:

Hi, Rob!

I just reviewed the current state of the book because I read it in 2020. What can I say: the author has reworked the text significantly! But his style is still very "outsider-ish", at least in the Elm part :)

In the real world, practically nobody uses lenses in Elm. In fact, it is considered as an anti-pattern to go deeper inside structures beyond one level. One should delegate any nested updating to the corresponding update functions - basically this is how most of the code is structured nowadays.

Also any try to bring Monoids and Categories into the project will be rejected vigorously, because "we use Elm, not Haskell!". You can do that stuff in your own projects, of course, bau that's all :(

Even more, I should note that custom operators are removed from the Elm completely, so now you just can't define all those "..>>" goodies...

P.S. About more helpful error messages. Elm is just that small to be able to have friendly error messages for common beginner's mistakes. But the whole friendliness disappears when you are starting to use a little more complex types. But also that simplicity of the language will limit you pretty soon, if you are familiar with typed FP in Haskell/OCaml/Scala/PureScript/Reason/... - there are some fundamental limitations like a luck of high-kinded types and ad-hoc polymorphism (typeclasses).

In my opinion, Elm still can be used as an entry-level FP language or a tool for pretty simple apps. I am still using it for the hobby stuff and for the teaching. But I'm always ready to switch to ClojureScript (personal preference for the ecosystem) in projects and to Haskell in classes :)


Собственно, сейчас я книгу могу даже рекомендовать. Но не как руководство по Elm и тому, как на нём принято писать. Зато как введение в некоторые концепции ФП — в таком ключе она может и сработать! Только Elm надо брать не самый свежий, потому что автор использует пользовательские инфиксные операторы, а их в Elm уже нельзя описывать (ок, есть errata для 0.19 — с 2020 есть, а за годы-то можно было уже и убраться).
🤔1
Меня тут упомянули в выпуске Flatmappers в контексте моей публикации про Scala Toolkit. Непонятно только, хорошим словом меня помянули или не очень — всё же я пошутил про то, что в Scala мире берут на вооружение то, что в стране Haskell уже отбросили. Собственно, важное прозвучало: то, что не удалось сделать хаскелистам, не должно останавливать скалистов — у этих-то может и получиться! Так что живём дальше, посматриваем.
🔥2
Вышел на новую работу, а тут всё по классике

.../
helpers/
...
utils/
...
UsefulExtensions.kt


(в корне репы еще tools есть, но это хоть реально директория с программками)

Помню, как цать лет назад на тогдашнем месте целые срачи были по поводу того, делать ли utils, helpers или оба частью корпоративного стандарта.
3
Занятно, что прямо в официальной документации к Swift наш любимый Emacs упоминается прямо рядом с Xcode!

И там же присутствует приличное руководство по настройке:

- пакеты нужные ставятся через use-package,
- для "умных штук" используется language server (правда, с lsp-mode, а не с eglot, но и eglot работает, судя по тому, что я нагуглил)

Я тут в рамках своего эксперимента по упаковке shell-scripts в самодостаточные программы решил ещё и Swift палочкой потыкать, и ничего — вполне приятно! LSP не заводил, обошёлся без него пока, но остальной инструментарий работает хорошо, Xcode трогать не пришлось вообще.
👍61
А забавно! Вся библиотечка описана в literate стиле с пояснением, что, как и почему.

Более того, это же вёрстка в стиле заметок к тексту Cornell-style! Именно эссе по методологии literate programming нечасто в таком виде публикуют. А зря – для таких вот "текстов", которые читаются строго сверху вниз, этот стиль очень даже подходит, мне кажется!

Про Cash узнал из переписки с ChatGPT (вот
она, та переписка). Спрашивал про легковесные JS-штуки, для использования которых не нужно настраивать сборку чего-либо. Получил помимо любимого мной HTMX и известной много кому AlpineJS ещё и ссылку на Cash, буду знать :)
Чё, посоны(ки), АдвентОфКод?
> given ('day-01.input'.IO.lines>>.split(/\s+/)>>.Int).cache { (($_>>.[0].sort <<->> $_>>.[1].sort)>>.&abs).sum }
2086478
😱6
BTW, DOOM сегодня 31 год исполнился. Знаковая игра в том числе и для меня лично — очень хотел в неё поиграть на своём 8x386, но тормозила она на нём знатно 🙈
👍2🔥1
Вчерашняя задачка (вторая часть дня 24) из Advent of Code была решена пристальным просмотром выхлопа GraphViz, для которого я сгенерил .dot-файлик.

На Reddit народ решал "с помощью Ctrl+F" то есть тоже глазами вычитывали почти все (правда, кто-то запарился и сделал симуляцию схемы в FPGA и прочих логических симуляторах).

А для меня сработал принцип "правильная визуализация — половина решения", сработал на 200% в данном случае :Р
🔥8👍3
TIL, что GitHub ещё и STL умеет рендерить нынче (у меня 3D-принтера нет, так что я как-то это новшество упустил). Узнал о такой функциональности, решив взглянуть, как выглядит результат генерации этих весёлых ёлочных игрушек: https://github.com/joe-warren/christmas-ornaments/blob/main/haskell-ornament.stl
🤯31
С Наступающим!
jshell> Stream.iterate("         1", x -> x.replaceFirst(" 1", "101")).takeWhile(x -> x.startsWith(" ")).forEach(System.out::println)
1
101
10101
1010101
101010101
10101010101
1010101010101
101010101010101
10101010101010101


BTW, jshell прям нормальный, выводит типы подвыражений и методы автодополняет :)
7👍1
Вот смотрю я на эти множественные доллары в Kotlin и хихикаю. Можно же просто экранировать ("\$") и всегда можно было. Или разрешили бы удваивать служебный символ, как много где давно сделано. А ещё можно было требовать всегда использовать {} — да, чуть более шумно, но не критично же! Зато встретить литерал с "${" сложнее и тут мы бы обошлись слэшем.

Удвоение как способ экранирования, например, в Python используется в обоих вариантах шаблонизации строк и в f-строках тоже. {, }, %, \ удваиваются для отмены действия. Можно (хоть и не нужно) делать всякие "отложенные подстановки":

>>> "%%%%s %%s %s" % 1 % 2 % 3
'3 2 1'
>>> "{{{{}}}} {{}} {}".format(1).format(2).format(3)
'3 2 1'
>>> f"%s {{}} {1}".format(2) % 3
'3 2 1'


Третий пример тут совсем шуточный, просто ради использования всех трёх способов форматировать строки в Python. Да-да, "There should be one-- and preferably only one --obvious way to do it." то самое 🌚
👍2😱1
Apple Script, конечно, в стиле COBOL дизайнили. Вот как я должен из головы написать такое?

set firstFolder to item 1 of input

tell application "Finder"
set fileList to every file of firstFolder whose name extension is "mp3"
end tell


Я ChatGPT попросил, естественно. В три итерации вида "— Падает так-то. — А, ну да, сделай так" получилось научить Apple Music играть папку как плейлист. Но только одну. И когда захочу все выделенные директории пройти, то Чада же буду просить, потому что не понимаю, как и куда сходу вписать что-то вроде "of every item in input".

Я ещё и ввожу этот код в Automator, который синтаксис подсвечивает только после нажатия кнопки с молотком (у неё нет тултипа, видимо "Build"). А если ты редактируешь код после подсвечивания, то новые кусочки будут без подсветки. Причём подсветка сделана в стиле WYSIWYG в Word, т.е. подсвеченные фрагменты перемежаются с новыми прямо внутри слова, например! А ещё иногда вводишь текст, курсор прыгает сам в следующую строчку и портит её 🙈 И при этом интерфейс автоматора тормозит как Word тормозил под Win'95 на первых Pentium.

И да, через shell scripting интероп между эппловыми программами не описать просто так. И в интерфейс программ описанные скрипты не встроить без Автоматора (или каких-то приседаний). Вот уже и папочка "Send to" в винде, куда достаточно было кинуть ярлыки на обычные скрипты, кажется верхом удобства.
👍4
Нашёл недавно один подкаст. Автор выкладывает его на PeerTube с заглушкой вместо видео. А я хочу именно слушать. Подкасты у меня играет PodcastAddict, ему можно скормить RSS из PeerTube и выпуски подтянутся. Вот только mp4 в этом конкретном случае какие-то битые: длительность неверная, индексирование не работает, так что ни дослушать потом нельзя, ни отмотать в другой раз до нужного места.

Решил скачать и сконвертировать, сначала даже обошёлся curl+sed+ffmpeg+bash. Но в итоге получил пачку mp3 с именами навроде UUID. Надо было переименовывать или тегать.

Пробовал таки потегать да так, чтобы PodcastAddict подхватил нормально даты и заголовки. Взял Python+mutagen, попробовал. Проиграл. Даты в ID3 вообще прокляты, потому что никто не знает, какие фреймы конкретная программа читает. А заголовки PodcastAddict просто игнорит, если подкаст берётся из локальной директории 🤮 Ну или ищет другой фрейм, про который я не знаю. Хоть файлы переименовывай, чего не хотелось бы, потому что автор подкаста поленился в едином стиле назвать выпуски и там полный треш, так что придётся ещё и вычищать от недопустимых символов и обрезать.
👍5
Ха-ха, всё ещё умею верстать на табличках с colspan/rowspan!

Забавно, что эволюция совершила виток и появился grid layout. Потому что не всегда хватает или просто неудобно использовать вложенные комбинации columns и rows.

А ведь когда-то деды закруглённые кнопки делали табличками 3x3! Но это от бедности делали, конечно (или нет).
🔥43
Задумался тут, а можно ли получить heatmap для файлов в репозитории Git. Конечно же, есть готовые решения, но я решил обойтись man и моими скромными навыками использования core utils.

git log --name-status --pretty=format: | \
awk "\$1 {print \$2}" | \
sort | uniq -c | sort -n


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

Да, тут двойная сортировка, но так уж вышло, что uniq не умеет считать уникальные строки, только если повторы идут подряд. А вторая сортировка уже использует количества повторов и "арифметический" порядок.

awk тут просто печатает второй столбец, если первый не пуст. Так я отбрасываю пустые строчки, получающиеся при выводе "пустой" информации о коммите ("format:").

По хорошему, надо бы учитывать переименовывание и удаление, поскольку Git это даже отображает при выводе с флагом "--name-status". Но мне было лень такие тонкости учитывать. Вот когда надумаю сделать на Python скриптик, который бы дерево проекта выводил и подкрашивал оттенками красного "горячие" файлы — тут уже можно будет и заморочиться 😉
🔥4👍1
В кои-то веки пошёл ванильного JS под браузер написать немножко. Решил, раз всё равно WASM тащу, то почему бы async/await не заюзать и данные через fetch не поскачивать. И проиграл, конечно. Потому что fetch не только сам асинхронно работает, что понимаемно, но и body отдаёт как stream, что тоже понимаемо, но именно в браузере неудобно!

На Node можно сделать

for await (const chunk of response.body) {
// ...
}


Не то чтобы каждый раз хочется чанками читать, но тут хотя бы синтаксически само вычитываение компактно сделано. Вот только в браузерах response.body — не асинхронный генератор! Нужно явно достать Reader и подёргать его в цикле, у меня такой костыль вышел:

const fetchAndRead = async (url) => {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let data = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
data += decoder.decode(value, { stream: true });
};
data += decoder.decode();
return data;
};


А я-то просто slurp хотел сделать 🙉. Кажется, что надо было забить и по-старинке взять XMLHttpRequest, хоть с ним тоже строчек на пять код потянет.
😱2
А ещё WASM можно только сервером раздать, поэтому "просто HTML cо статикой в соседней папке" не получится сходу сделать. Придётся как base64 инлайнить, а этому ещё предстоит научиться. И мегабайт на выходе будет больше, хоть и сжиматься оно будет неплохо.

И всё для чего? Просто чтобы сделать штуку, которую придумал за чаем — "jq, но в браузере и чтобы как самодостаточная страничка работало чисто на клиенте". Сам jq в WASM уже обернули до меня, я только стою на плечах гигантов.

Вот так выглядит MVP уже сейчас (вёрстка, как водится, инженерная; "WJQ" читать как "Вжик"):
9
Вот как давече упомянутый Вжик выглядит нынче:

http://astynax.me/wjq/?url=example.json&query=.message.words+%7C+map%28.word%29&flags=-c&pretty=true — жать [Fetch], потом жать [Execute]. Первый раз может грузиться чуть долговато, потому что WASM надо стянуть, но м.б. у GitHub и вполне быстрый CDN для статики 🌚

JSON можно накопипастить, не обязательно фетчить.

Кнопка [Remember] сохраняет параметры (но не JSON-данные!) в URL, чтобы можно было в закладки положить или поделиться, либо просто использовать кнопку "Назад" браузера как "Undo" 😎 History API используется максимально топорно, я специально всё в URL положил, а не в state, потому что так захотелось 🥴 Опять же, я в будущем могу захотеть трекать то, что вы там вжикаете👺

Пока писал это вот, подумал, что можно ещё сделать "скопировать как shell command" (curl + jq), элементарная фича же!

Ну и есть желание делать "fork", через открытые текущей конфигурации в новой вкладке. А ещё думал о том, чтобы можно было по кнопке добавлять следующий "шаг" — ещё одну query уже применительно к "Result" в виде нового fieldset — и так произвольное количество раз. Потом такие шаги можно было бы схлопывать в один, поскольку запросы jq композируются!

P.S. Чую, что в какой-то момент добавится поведение "выполнить отсюда и вниз" и получится кривенький вариант Jupyter Notebook или Org Mode, но зато прямо в браузере

P.P.S. Да, я в курсе, что Jupyter уже давно упаковали в WASM и можно его гонять локально. Но у меня тут Smol Software, а не этот ваш Bloatware!
👍3🤔2
Подумалось тут, что jq, это неплохо, конечно, но м.б. имеет смысл класть выхлоп jq в глобальную переменную — хоть в атрибут того же document. Чтобы из консоли DevTools можно было бы данные потыкать силами JavaScript.

Правда, инструментарий у JS не самый располагающий к удобному использованию в REPL. Может быть, стоит какой-нить lodash подтянуть, или что там носят ещё?

И да, это ещё один шажок в сторону Jupyter. Впрочем, я бы не отказался от того, чтобы можно было эдакий data playground иметь прямо в браузере. Только чтобы он был local first хотя бы в базовой комплектации — поэтому-то я и хочу свой wjq упаковать в самодостаточный HTML. Можно и что-то более развлекательное соорудить из p5, d3 и других визуальных штук. Но важно, чтобы оно было "no build" и максимально полноценно работало в offline. Есть, над чем подумать.
🔥3