Стрим разработка под Android на Kotlin. Доделываю cljs REPL https://youtu.be/vHXLRgbAOjQ
Пару дней назад запустили открытую бету ClojureScript REPL Replete 2.0 https://twitter.com/mfikes/status/1086743669825261569
Помимо первого релиза для Android добавили неймспейсы для работы с файловой системой (
Помимо первого релиза для Android добавили неймспейсы для работы с файловой системой (
replete.io) и сетью (replete.http).Недавно дошли руки попробовать построить нативный UI по принципу Flutter (без нативных контролов, все сделать с нуля на OpenGL).
Первая мысль была взять простую библиотеку типа NanoVG, но такие простенькие решения не предусматривают батчинг обращений к GPU, часто не умеют размечать текст и в целом не делают многих оптимизаций, которые нужны для «богатого» UI.
В общем попробовал подключить Skia (рендерер в Chrome и Flutter). Если коротко, то C++ — боль. Особенно писать к нему биндинги.
Для лайаута взял Yoga (сабсет flex box, испольузется в React Native) и в целом вышшло неплохо, примеры можно посмотреть вот здесь https://romanliutikov.com/native-ui-part-1.html
Интересная альтернатива — новый рендерер в Firefox WebRender. Пишут, что почти все идет через GPU и вроде бы как выдает заоблачный FPS. Плюс, что написан на Rust. Минус — все еще в разработке. Но уже есть пару примеров использования в продакшн в качестве бекенда для GUI библиотек.
Другой вопрос — какой выбрать скриптовый язык? Сначала подумал про лиспы (SBCL, ECL или Carp), потом вспомнил про Lua. Но в и тоге думаю забиндить рендерер под V8/Node.js, написать кастомный reconciler для React и использовать кложуру и экосистему реакта для написания UI.
Если подумать, то производительность скриптового языка не играет роли. Изменения в UI не происходят с частотой обновления экрана, нормальные анимаци никогда не контролируються из UI потока. Узкое горлышко — это рендерер и иногда биндинги. Например Chrome многие вещи делает на CPU, а React Native гоняет сериализированные данные между JS и Native. Отсюда и хреновая производительность.
В общем интересный эксперимент, посмотрим насколько у меня хватит мотивации, может сделаю пару стримов.
Первая мысль была взять простую библиотеку типа NanoVG, но такие простенькие решения не предусматривают батчинг обращений к GPU, часто не умеют размечать текст и в целом не делают многих оптимизаций, которые нужны для «богатого» UI.
В общем попробовал подключить Skia (рендерер в Chrome и Flutter). Если коротко, то C++ — боль. Особенно писать к нему биндинги.
Для лайаута взял Yoga (сабсет flex box, испольузется в React Native) и в целом вышшло неплохо, примеры можно посмотреть вот здесь https://romanliutikov.com/native-ui-part-1.html
Интересная альтернатива — новый рендерер в Firefox WebRender. Пишут, что почти все идет через GPU и вроде бы как выдает заоблачный FPS. Плюс, что написан на Rust. Минус — все еще в разработке. Но уже есть пару примеров использования в продакшн в качестве бекенда для GUI библиотек.
Другой вопрос — какой выбрать скриптовый язык? Сначала подумал про лиспы (SBCL, ECL или Carp), потом вспомнил про Lua. Но в и тоге думаю забиндить рендерер под V8/Node.js, написать кастомный reconciler для React и использовать кложуру и экосистему реакта для написания UI.
Если подумать, то производительность скриптового языка не играет роли. Изменения в UI не происходят с частотой обновления экрана, нормальные анимаци никогда не контролируються из UI потока. Узкое горлышко — это рендерер и иногда биндинги. Например Chrome многие вещи делает на CPU, а React Native гоняет сериализированные данные между JS и Native. Отсюда и хреновая производительность.
В общем интересный эксперимент, посмотрим насколько у меня хватит мотивации, может сделаю пару стримов.
Неплохая книга (free!) о дизайне интерпретаторов от создателя Dart. Описывает весь путь от текстовой программы до виртуальной машины http://www.craftinginterpreters.com
Кстати автор UglifyJS лиспер и в его блоге есть серия статей о создании интерпретатора на JS, сжато и очень доступно http://lisperator.net/pltut/
Кстати автор UglifyJS лиспер и в его блоге есть серия статей о создании интерпретатора на JS, сжато и очень доступно http://lisperator.net/pltut/
Начали публиковать доклады с недавней ClojureD. Отличный доклад интро в triple store бд (datomic, datanoscript, etc.) https://www.youtube.com/watch?v=tbVwmFBnfo4
Новое видео на канале https://youtu.be/VlLydB0rohQ Эксперименты с Hiccup, спекой и реакт с хуками.
Второе видео из серии будет опубликовано в понедельник, но для подписчиков канала запощу его сюда сегодня. Это продолжение серии скринкастов о создании cljs враппера для React https://youtu.be/Hqd7lbIc668
Получить доступ к остальным видео можно по подписке на Patreon https://www.patreon.com/bePatron?c=1239559
P.S. Если что, то подписка существует не для зароботка, а для того, чтобы понимать насколько это интересно остальным.
Получить доступ к остальным видео можно по подписке на Patreon https://www.patreon.com/bePatron?c=1239559
P.S. Если что, то подписка существует не для зароботка, а для того, чтобы понимать насколько это интересно остальным.
Новый API хуков в React оказался дубовым. Мемоизация
Забавно, что это аргументируется тем, что разработчики реакта хотят этим предотвратить использование deep equals со стороны пользователей библиотеки. В то же время они отсекают возможность нормально использовать API с кастомными структурами данных.
То есть вышло как обычно: попытки угадать что-то наперед в итоге кому-то выходят боком. Посмотрите что там нагородили в комментариях.
https://github.com/facebook/react/issues/14476
useEffect работает через shallow equal аргументов, что не дает возможность использовать иммутабельные значения, у которых семантика сравнения совершенно другая. Вместо этого можно было бы дать возможность передавать компаратор.Забавно, что это аргументируется тем, что разработчики реакта хотят этим предотвратить использование deep equals со стороны пользователей библиотеки. В то же время они отсекают возможность нормально использовать API с кастомными структурами данных.
То есть вышло как обычно: попытки угадать что-то наперед в итоге кому-то выходят боком. Посмотрите что там нагородили в комментариях.
https://github.com/facebook/react/issues/14476
Был вопрос по поводу вчерашнего поста: что ломает shallow equals с иммутабельными данными, если значения по ссылке равны?
Сравнение по ссылке работает верно только в том случае, если оба значения действительно ссылаються на один и тот же объект в памяти. То есть здесь все ок:
Но два разных объекта в памяти никогда не будут идентичны:
На самом деле теоретически это возможно сделать. Если все значения иммутабельны, то по факту нет смысла аллоцировать в памяти структурно равное значение. Достаточно кэшировать такое значение и возвращать на него ссылку при создании структурно равного. Это одна из оптимизация использования памяти с иммутабельными данными (кстати пришла к нам со времен лиспов https://en.wikipedia.org/wiki/Hash_consing). Но на деле выходит, что применять это глобально всегда приносит больше вреда. Поэтому такие штуки обычно выносят в библиотеки и применяют к отдельным частям системы.
P.S. С вопросами пишите в личку @roman01la
Сравнение по ссылке работает верно только в том случае, если оба значения действительно ссылаються на один и тот же объект в памяти. То есть здесь все ок:
(let [m1 {:x 1} m2 m1] (identical? m1 m2)) ;; trueНо два разных объекта в памяти никогда не будут идентичны:
(identical? {:x 1} {:x 1}) ;; false. Хотя структурно они равны: (= {:x 1} {:x 1}) ;; true. Случай когда разные значения в памяти равны структурно возникает довольно часто, поэтому только сравнением по ссылке не обойтись.На самом деле теоретически это возможно сделать. Если все значения иммутабельны, то по факту нет смысла аллоцировать в памяти структурно равное значение. Достаточно кэшировать такое значение и возвращать на него ссылку при создании структурно равного. Это одна из оптимизация использования памяти с иммутабельными данными (кстати пришла к нам со времен лиспов https://en.wikipedia.org/wiki/Hash_consing). Но на деле выходит, что применять это глобально всегда приносит больше вреда. Поэтому такие штуки обычно выносят в библиотеки и применяют к отдельным частям системы.
P.S. С вопросами пишите в личку @roman01la
Второй вечер залипаю на видео материалы с Ричардом Фейнманом, завидую его умению объяснять так доходчиво:
https://www.youtube.com/watch?v=VdYujzyaX68
https://www.youtube.com/watch?v=P1ww1IXRfTA
https://www.youtube.com/watch?v=EKWGGDXe5MA
Also, из WTF в программировании:
- GHC ограничивает размер таплов до 62, дальше segfault 😀 https://github.com/ghc/ghc/blob/e3f341f334d89c88f388d8e864ed8762d0890a64/libraries/ghc-prim/GHC/Tuple.hs#L170
- Rust 32 https://github.com/diesel-rs/diesel/blob/57e17f224b7938b33487d2a0e6ba83fb524dd42e/diesel/src/macros/tuples.rs
- Scala 22
И посмотрите на прекрасные елки из сигнатур 😀
https://www.youtube.com/watch?v=VdYujzyaX68
https://www.youtube.com/watch?v=P1ww1IXRfTA
https://www.youtube.com/watch?v=EKWGGDXe5MA
Also, из WTF в программировании:
- GHC ограничивает размер таплов до 62, дальше segfault 😀 https://github.com/ghc/ghc/blob/e3f341f334d89c88f388d8e864ed8762d0890a64/libraries/ghc-prim/GHC/Tuple.hs#L170
- Rust 32 https://github.com/diesel-rs/diesel/blob/57e17f224b7938b33487d2a0e6ba83fb524dd42e/diesel/src/macros/tuples.rs
- Scala 22
И посмотрите на прекрасные елки из сигнатур 😀
Опубликовал третий выпуск скринкастов. В этот раз о мемоизации компонентов https://youtu.be/H9yyjdRhstU
Пример как React мог бы сообщать о «вредительских» оптимизациях в
shouldComponentUpdate или React.memo https://codesandbox.io/s/kmpxv86j85Из интересного:
Опубликовали технические детали гугловой Fuchsia OS. Перспектива писать UI на Dart не радует, но в целом выглядит интересно https://bzdww.com/article/163937/
Java 12 релизнулась с Shenandoah GC («ultra-low pause time garbage collector») https://wiki.openjdk.java.net/display/shenandoah/Main
Сообщество кложуристов все больше донатит на open source. Теперь выделяют грант в $3000 на поддержку проектов https://www.clojuriststogether.org/news/february-2019-monthly-update/
Опубликовали технические детали гугловой Fuchsia OS. Перспектива писать UI на Dart не радует, но в целом выглядит интересно https://bzdww.com/article/163937/
Java 12 релизнулась с Shenandoah GC («ultra-low pause time garbage collector») https://wiki.openjdk.java.net/display/shenandoah/Main
Сообщество кложуристов все больше донатит на open source. Теперь выделяют грант в $3000 на поддержку проектов https://www.clojuriststogether.org/news/february-2019-monthly-update/
Пробовали когда-нибудь обрабатывать видео в браузере? Chrome дает ряд API для захвата видео и аудио источников, поэтому, в принципе, это возможно.
Комбинировать видео с текстом, изображениями и эффектами можно в WebGL: видео стримится в канвас + покадровый рендрер в текстуры и композинг (https://github.com/bbc/VideoContext) + покадровый захват (https://github.com/spite/ccapture.js/) и экспорт обратно в видео (https://github.com/thenickdude/webm-writer-js/).
При желании это можно распараллелить в воркерах используя OffscreenCanvas.
Единственная проблема сейчас — браузер не дает точный тайминг начала и конца видео, поэтому поймать первые и последние кадры практически невозможно. Альтернатива резать видео на кадры в ffmpeg скомпиленом в wasm (https://github.com/Kagami/ffmpeg.js)
Комбинировать видео с текстом, изображениями и эффектами можно в WebGL: видео стримится в канвас + покадровый рендрер в текстуры и композинг (https://github.com/bbc/VideoContext) + покадровый захват (https://github.com/spite/ccapture.js/) и экспорт обратно в видео (https://github.com/thenickdude/webm-writer-js/).
При желании это можно распараллелить в воркерах используя OffscreenCanvas.
Единственная проблема сейчас — браузер не дает точный тайминг начала и конца видео, поэтому поймать первые и последние кадры практически невозможно. Альтернатива резать видео на кадры в ffmpeg скомпиленом в wasm (https://github.com/Kagami/ffmpeg.js)
Открыта продажа билетов на Heart of Clojure, 2го августа в Бельгии https://heartofclojure.eu/
Пару недель назад опубликовал последний скринкаст о создании cljs враппера для React (все видео в плейлисте https://www.youtube.com/playlist?list=PLHOTezm7WWkl4rfd6j0fsL65DrxfWz4XM). В итоге библиотека практически добралась до статуса production-ready, поэтому грех было не довести ее до ума: https://github.com/roman01la/uix
Помимо uix уже существует еще два враппера для React с хуками, саспенсом и т.д. Изначально у каждого был свой интерпретатор Hiccup, но в итоге мы взяли самый быстрый из Reagent и коллективными усилиями выжали из него максимум, даже немного обогнали оригинал. По бенчмаркам интерпретация + построение VDOM вышли ~2.5x медленее чистого React, тогда как Reagent примерно в 3.5x медленее.
Ну и реализация враппера на основе хуков много проще и понятнее, чем поверх классов в JS.
Из интересного конкретно в библиотеке uix:
Есть опциональная прекомпиляция Hiccup макросом. Компилятор сделан на основе форка компилятора Hicada с фолбеком на интерпретацию когда невозможно установить тип значения в compile-time. Type inference в cljs сегодня может давать информацию по типам примитивных значений, что уже сокращает количество вставок интерпретатора в скомпилированный Hiccup (https://kevinlynagh.com/notes/fast-cljs-react-templates/). Чтобы еще больше сократить вставки я экспериментирую с рантайм проверкой через clojure.spec, выходит, что в compile-time функции со спекой пропускают проверку типов откладывая в рантайм проверку спекой.
Есть поддержка ленивой подгрузки компонентов через React.lazy + Suspense, оформлена в виде знакомой кложуристам формы
И есть возможность перехватывать интерпретацию Hiccup. Например чтобы использовать CSS-in-JS в виде инлайновых стилей. тогда можно писать вот так:
Пример всего этого можно посмотреть вот здесь: https://github.com/roman01la/uix/blob/master/dev/uix/example.cljs
Помимо uix уже существует еще два враппера для React с хуками, саспенсом и т.д. Изначально у каждого был свой интерпретатор Hiccup, но в итоге мы взяли самый быстрый из Reagent и коллективными усилиями выжали из него максимум, даже немного обогнали оригинал. По бенчмаркам интерпретация + построение VDOM вышли ~2.5x медленее чистого React, тогда как Reagent примерно в 3.5x медленее.
react x 22989 ops/s, elapsed 435ms
uix-interpret x 9166 ops/s, elapsed 1091ms
reagent-interpret x 6680 ops/s, elapsed 1497ms
Ну и реализация враппера на основе хуков много проще и понятнее, чем поверх классов в JS.
Из интересного конкретно в библиотеке uix:
Есть опциональная прекомпиляция Hiccup макросом. Компилятор сделан на основе форка компилятора Hicada с фолбеком на интерпретацию когда невозможно установить тип значения в compile-time. Type inference в cljs сегодня может давать информацию по типам примитивных значений, что уже сокращает количество вставок интерпретатора в скомпилированный Hiccup (https://kevinlynagh.com/notes/fast-cljs-react-templates/). Чтобы еще больше сократить вставки я экспериментирую с рантайм проверкой через clojure.spec, выходит, что в compile-time функции со спекой пропускают проверку типов откладывая в рантайм проверку спекой.
Есть поддержка ленивой подгрузки компонентов через React.lazy + Suspense, оформлена в виде знакомой кложуристам формы
require.(require-lazy '[uix.components :refer [ui-list]])И есть возможность перехватывать интерпретацию Hiccup. Например чтобы использовать CSS-in-JS в виде инлайновых стилей. тогда можно писать вот так:
[:button {:css {:font-size 14}} "Submit"]Пример всего этого можно посмотреть вот здесь: https://github.com/roman01la/uix/blob/master/dev/uix/example.cljs
Апдейт по UIx. Забыл включить кэш в интерпретаторе, теперь рендер в 2х быстрее реагента и всего в 1.5х медленнее ванильного реакта. Никогда не думал, что можно выжать столько из cljs, при этом сохранив относительную идиоматичность в коде.
Успешно "украл" и адаптировал сериализатор Hiccup из Rum для серверного рендеринга на JVM. Поверх него сделал возможность отдавать разметку кусками пока она рендерится, например пушить в manifold stream и отдавать на клиент через aleph.
Добавил синтаксис в Hiccup для порталов в реакте [:-> element :#selector] и сделал для них рендеринг на сервере, но оказалось что сам реакт не умеет этого делать, поэтому пришлось откатить :)
В целом вышла неплохая библиотека, параллельно обкатываю на пэт проекте.
https://github.com/roman01la/uix
Успешно "украл" и адаптировал сериализатор Hiccup из Rum для серверного рендеринга на JVM. Поверх него сделал возможность отдавать разметку кусками пока она рендерится, например пушить в manifold stream и отдавать на клиент через aleph.
Добавил синтаксис в Hiccup для порталов в реакте [:-> element :#selector] и сделал для них рендеринг на сервере, но оказалось что сам реакт не умеет этого делать, поэтому пришлось откатить :)
В целом вышла неплохая библиотека, параллельно обкатываю на пэт проекте.
https://github.com/roman01la/uix
GitHub
GitHub - roman01la/uix: Idiomatic ClojureScript interface to modern React.js
Idiomatic ClojureScript interface to modern React.js - roman01la/uix
О динамических стилях и прекомпиляции макросом.
Стили можно прекомпилировать макросом для оптимизации в рантайме. Например трансформировать декларацию в виде мапы в литерал JS объекта, чтобы обойтись без этой трансформации в рантайме:
Статические части стилей можно вообще писать в файл и оставлять только динамические. Обычно 90% стилей статичны, поэтому имеет смысл убрать их из райнтайма:
Можно пойти еще дальше и перенести в статику константные значения типа
В таком подходе есть минус — записывая в файл на этапе компиляции нет возможности убрать неиспользуемые стили из рантайма.
Стили можно прекомпилировать макросом для оптимизации в рантайме. Например трансформировать декларацию в виде мапы в литерал JS объекта, чтобы обойтись без этой трансформации в рантайме:
{:top 10} -> {top: 10}.Статические части стилей можно вообще писать в файл и оставлять только динамические. Обычно 90% стилей статичны, поэтому имеет смысл убрать их из райнтайма:
10 :color styles/white}
{:color styles/white}
coponent.css -> .component {top: 10}
Можно пойти еще дальше и перенести в статику константные значения типа
(def ^:const white "#fff") (обязательно нужен хинт ^:const), тогда такие значение можно определить анализатором (cljs.analyzer) и заинлайнить в статическую часть стилей. Это хороший кейс когда есть набор переиспользуемых цветов, размеров и т.д.В таком подходе есть минус — записывая в файл на этапе компиляции нет возможности убрать неиспользуемые стили из рантайма.