Fork me on GitHub
#clojure-russia
<
2017-01-09
>
artemyarulin06:01:00

http://www.tiobe.com/tiobe-index/ говнорейтинг, но кложура на 47 месте (раньше вроде в топ 50 даже не была) ну и да

The main drivers behind Go's success are its ease of learning and pragmatic nature. It is not about theoretical concepts such as virtual inheritance and nomads (!?) but about hands-on experience

niquola07:01:39

beating averages ;)

andre07:01:46

а когда обычно результаты по клоджар сюрвей?

andre07:01:52

интересно же

dottedmag07:01:59

Господа, а у кого-нибудь случается низкоинтенсивный, но перманентный баттхёрт из-за того, что всякие реагенты всё ещё заставляют разбивать логику на куски "нажали кнопку -> вызвался хэндлер, чего-то посчитал, обновил shared state; нажали другую кнопку -> вызвался другой хэндлер, поднял из shared state недосчитанное, чего-то ещё посчитал"?

niquola07:01:05

А бывает не так? ;)

dottedmag07:01:35

Я пытаюсь понять, как скрестить Ричевую идею "вернуть всю логику в одно место" из его core.async-презенташки и при этом сделать это так, чтобы персистентные данные а-ля Ом были синхронизированы с этой логикой даже при рефреше страницы (когда состояние сохраняется, а весь core.async-стейт теряется).

niquola07:01:44

Ивент заэмитили и все пересчитали?

dottedmag08:01:19

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

dottedmag08:01:55

Проблема в том, что этот го-блок что-то рисует (через стейт), и если стейт персистить в localStorage, а страничку обновить, то будет десинхронизация.

dottedmag08:01:54

Конечно, можно забить и сказать "всё, что складывается в этот кусок стейта - транзиентное, его мы не сохраняем; всё, что складывается в другой кусок стейта - персистентное, мы его через refresh тянем", и тогда, например, диалог, привязанный к go-блоку, после рефреша пропадёт. Но хочется же лучше.

niquola08:01:58

Превратить в данные

niquola08:01:31

Ты типа про некий воркфлоу?

dottedmag08:01:33

Бинго. Я уже смотрел в потроха core.async, но они слишком запутанные, чтобы их сторадж данных целиком положить в атом реагента 🙂

niquola08:01:53

Пример приведи

dottedmag08:01:29

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

dottedmag08:01:39

Не хочется это размазывать на сто спагеттин.

dottedmag08:01:05

Вариант 1: весь этот state - transient, после рефреша страницы пропадёт. ОК, жить можно.

dottedmag08:01:53

Вариант 2, holy grail: весь этот state, включая стейт go-блоков его обслуживающих - persistent, после рефреша страницы продолжает, как был (если были XHR в полёте - отвалятся, но UI останется как есть, можно продолжать работать).

dottedmag08:01:05

Я нашёл Dragonmark и ago, которые делают что-то в этой же области, но ещё не покопал.

niquola08:01:19

Нужны чекпоинты для state и их сохранять как данные

dottedmag08:01:49

Там стейт хитрый - стектрейсы стейт-машин.

niquola11:01:58

Но твой-то стейт простой - или тоже сложный?

dottedmag11:01:08

Мой стейт может быть каким угодно, он под моим контролем.

dottedmag11:01:18

А вот стейт кор.асинк -- не совсем.

niquola11:01:45

А что у тебя в каналах живет?

misha11:01:19

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

misha11:01:16

функции перекладывания данных из канала в канал - стейтлесс, каналы - стейт.

dottedmag13:01:28

@nicola События от UI

dottedmag13:01:07

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

dottedmag13:01:16

И что будет после рефреша?

dottedmag13:01:28

Диалог будет (если он в состоянии хранится), а go-блока не будет.

artemyarulin13:01:30

дак сделай что евент меняет стейт

dottedmag13:01:34

И будет такой зомбик.

dottedmag13:01:07

И получится лапша "onclick=сделай мне так". это плохо.

dottedmag13:01:47

Получится вот эта картинка из Рича: http://endot.org/notes/2014-02-14-core-async-clojure/problem.png

artemyarulin13:01:49

не очень понимаю. Тут вроде просто - либо персистишь значения либо нет, либо имеешь их после рефреша или нет

niquola13:01:55

ну в таком подходе либо persistence либо каналы с салатовой логикой

niquola13:01:20

логику вообще лучше как можно pure сделать

niquola13:01:52

а что у тебя там за логика?

dottedmag13:01:18

Я ж выше приводил пример - логика аплоада картинок. Вот есть картинка, мы её повращали, пообрезали, но ещё не зааплоадили.

dottedmag13:01:43

Это не настоящий пример, а модельный для такого класса интерфейсов.

niquola13:01:08

Ну это вполне себе state который можно персистеть - если ты фотку можешь сохранить?

dottedmag13:01:35

Вот если я нахожусь в середине диалога редактирования, то у меня код находится в каком-то состоянии стейт-машины.

dottedmag13:01:04

Если зарефрешить, то это состояние пропадёт.

dottedmag13:01:31

Поэтому я и думаю, как персистить все эти отложенные стеки из core.async.

niquola13:01:44

Давай есть фотка, я ее повернул - у меня есть фотка + повернуть на 90%

niquola13:01:57

где state-machine?

dottedmag13:01:58

Хорошо. Юзер нажал кнопку аплоада, рисуется прогрессбар, аплоадятся фотки. Тут жмакнули рефреш.

dottedmag13:01:08

Прогрессбар остался (он же в стейте хранится).

dottedmag13:01:17

А код, который аплоадил, не остался.

dottedmag13:01:23

Кто будет его убирать?

niquola13:01:51

upload на сервер?

niquola13:01:17

Те в этот момент у тебя рассинк

dottedmag13:01:24

Да. И это просто пример.

dottedmag13:01:36

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

niquola13:01:38

ты можешь записать что собирался делать на клиенте - write ahead log

dottedmag13:01:46

Ну нет, это фигня.

niquola13:01:48

спросить что из этого уже сделал сервер

dottedmag13:01:51

Тогда я могу и лапшу сделать.

dottedmag13:01:22

Это всё не для того, чтобы сделать мою жизнь сложной, а наоборот 🙂 Если сделать мою жизнь простой не получается, то тогда я это делать не буду, а буду делать "discard transient on refresh"

niquola13:01:58

тогда выбрасывай этот стэйт - нафиг возится - если не важно

dottedmag13:01:14

Это и есть вариант 1 выше.

niquola13:01:46

я думал ты хочешь его сохранить - но если можно не сохранять - зачем заморачиваться

dottedmag13:01:00

Если не сохранять, то UI получается менее интересный.

dottedmag13:01:07

Но это не так страшно, можно и обойтись.

niquola13:01:11

ну вот и заходим на второй круг

dottedmag13:01:40

Собственно, проблема в том, что в этом случае "интересность" данных начинает влиять на структуру кода, а это нехорошо.

niquola13:01:10

у тебя есть сервер, клиент и неожиданный ребут страницы - это сложная задача - если хочешь консистентности

dottedmag13:01:22

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

dottedmag13:01:40

Ну вот я вижу, как это можно решить - засушиванием стейта core.async и регидрацией потом.

niquola13:01:48

ну ты не даешь гарантий на refresh - и все

niquola13:01:08

а как ты upload в серединке востановишь?

dottedmag13:01:39

после восстановления upload отвалится с ошибкой, это ясен пень. но upload может отвалиться с ошибкой и без рефреша.

dottedmag13:01:55

так что если у меня есть логика retry на клиенте для сетевых проблем, то upload пойдёт дальше.

niquola13:01:31

а разве можно заретраить file upload?

dottedmag13:01:01

не, другая задача: клиент выбрал пачку фоток и они улеглись в локальный стейт, а потом по upload они поехали XHR-ом на сервер.

dottedmag13:01:17

XHR после рефреша отвалится, как по ошибке сети.

artemyarulin13:01:51

если есть логика retry т.е. есть и стейт (не явный наверно) который говорит о том что картинка ушла на сервак или нет. В чем проблема сделать этот стейт явныМ и персистить? при рефреше логика та же как при ретрае - есть чо не загруженное - загружаем

niquola13:01:53

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

niquola13:01:11

ручками заново их запускаешь

dottedmag13:01:11

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

dottedmag13:01:33

я не хочу руками писать код, который смотрит "ага, мы дошли досюда".

artemyarulin13:01:47

дак а как ретрай работает у тебя?

dottedmag13:01:52

Потому что весь этот код уже есть в core.async

dottedmag13:01:36

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

dottedmag13:01:04

Как будто шелл-скрипт и wget, всё синхронное.

artemyarulin13:01:04

ну вот, не явный стейт ага

dottedmag13:01:35

Когда управление доходит до точки, в которой core.async паркует го-блок, core.async сохраняет этот стейт куда-то.

artemyarulin13:01:36

без кор.асинка по мне дак просто решается, а как с ним хз

dottedmag13:01:37

И я не хочу руками за кор.асинк делать то же самое 🙂

niquola13:01:54

Ну он не совсем это делает

dottedmag13:01:57

Потому что лапша!!!

niquola13:01:33

Нужен более детальный пример - так слишком абстрактно

niquola13:01:59

фоточка - upload - поворот - и посмотреть как ты это по каналам распихиваешь

niquola13:01:42

тебе может core.async не нужен 🙂

dottedmag13:01:11

Нужен. Если без него, то это пачка эвентхэндлеров. Я не хочу эвентхэндлеры писать, это код, вывернутый наизнанку.

dottedmag13:01:45

Рич тут всё правильно говорит: https://www.infoq.com/presentations/clojure-core-async

dottedmag13:01:02

В общем, проще забить. При рефреше недоделанное пропадает.

artemyarulin13:01:28

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

niquola13:01:32

Корасинк это оркестрация множества процессов, и это стэйтфул причем имплицитно

misha13:01:09

тебе стейт нужно поделить на сохраняемый и несохраняемый. повороты/ресайзы картинки - сохраняемый, флаг "начался ли аплод" - нет. показывать прогресс бар - зависит от флага. при рефреше: ресайзы все восстановятся, а флаг начала аплода заресетится в false ==> прогресс бар не появится

misha13:01:55

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

misha13:01:30

он только поможет заменить прямые вызовы функций на непрямые/ивенты/чо там еще

niquola13:01:15

если ты хочешь востанавливать состояние - то тебе нужно его и трэкать - @misha прав

misha13:01:14

стейт в атоме, и атому пофигу кто и откуда его обновляет: из колбека или из канала

misha13:01:27

но я бы и сам посмотрел, как взрослые дядьки используют корасинк "по-назначению". в юае пока что я видел только юзкейс заменяющий {:on-click #(logic)} на {:on-click #(tell that channel I want to execute logic)}

dottedmag13:01:13

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

dottedmag14:01:29

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

niquola14:01:50

Ну можно еще в хэндлере написать (let [res (<! ch (xhr)] do something), посмешивать их и тп

dottedmag14:01:52

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

misha14:01:06

тоже кор-асинк задача? kappa

dottedmag14:01:18

Таки-да. И с теми же проблемами.

leov14:01:49

привет! а я пришёл порыдать. автор микрофреймворка-для-веб chestnut сейчас впиливает 'system', и не любит библиотеку 'mount' 😞 https://github.com/plexus/chestnut/issues/184

dottedmag14:01:50

Вот не хочу я персистить 100500 чатов с клиентами руками, и делать стейтмашину руками на эвентхэндлерах.

misha14:01:19

это проблемы разделения стейта на восстанавливаемый и нет, типа (defonce) и (def)

misha14:01:58

только в этом случае "рефреш страницы" - это рефреш кода фигвилом

misha14:01:05

принцип тот же

dottedmag14:01:11

Ну да. Точнее, проблема того, что состояние программы не reified.

dottedmag14:01:19

Так что я не могу сделать снэпшот.

artemyarulin14:01:31

@leov дак это вроде же темплейт просто? Инит сделал, потом поменял чо надо не?

misha14:01:34

мало, значит, в атом напихал всякого

dottedmag14:01:48

состояние программы

niquola14:01:07

если ты следишь за своим состоянием - то всегда из него можешь востановится - программа должна быть подчинена данным

niquola14:01:23

данные можно сериализовать и сохранить локально или на сервере

niquola14:01:59

и гранулярность ты выбираешь сам

dottedmag14:01:01

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

niquola14:01:13

это шаг назад 🙂

misha14:01:27

ты жеж должен стремиться к pure-functions kappa и минимизировать сайд эффекты. например с аплодом - единственный сайд эффект - аплод. всё остальное можно сериализовать в атом (реф)

artemyarulin14:01:03

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

misha14:01:21

как это? kappa

leov14:01:21

@artemyarulin да, но следующая версия будет на системе. я не понимаю, почему просто люди могут любить систему вместо маунта. это же джава джава джава джава джава борщ борщ.. (

alexander_mihailov14:01:46

@leov вкусовщина. Форк тебе в помощь.

artemyarulin14:01:52

я не один из них не юзаю, мне норм troll

leov14:01:08

рельсы меня сильно "испортили".. (

misha14:01:19

@artemyarulin не достать из каналов, если эти каналы кто-то уже нахачил, и впадлу переделывать

misha14:01:39

а с нуля запекать стейт в каналы - это жеж тоже самое "руками"

leov14:01:40

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

artemyarulin14:01:54

@misha ну оно уже есть, выкидывать код жалко может 🙂

misha14:01:13

что же теперь делать? kappa

artemyarulin14:01:46

делать костыль, хлоп-хлоп-и-в-продакшен 🙂

andre14:01:26

хлоп-хлоп 🙂 лайт версия 🙂

misha14:01:33

главное назвать его "следующий уровень абстракции" opieop

artemyarulin14:01:41

цензура, вдруг тут дети 🙂

andre14:01:12

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

andre14:01:17

как хорошо что я сразу пришел в ре-фрейм

artemyarulin14:01:30

а там есть чо для контекста?

andre14:01:48

ну там вьюха сама подписывается на что ей надо

andre14:01:56

не надо ниче ей пробрасывать

artemyarulin14:01:27

ну надо будет ID какой пробросить все равно ж

artemyarulin14:01:11

а там вчера было обсуждения на тему дерева вроде как

<Root id=‘main’>
  <Link id=‘1’>
    <Link id=‘2>
      <Link id=‘3’/>
   </Link>
  </Link>

andre14:01:33

я про это

artemyarulin14:01:27

а ну ок, я краем глаза все читал, сори

misha14:01:52

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

andre14:01:49

ну да 🙂

leov19:01:40

гм. вопрос по кложе - я пробрасываю в джавный интерфейс листенеров каких-то событий свою функцию, которую в reify этого интерфейса дёргаю

leov19:01:59

когда я обновляю код этой функции - он не обновляется а каким-то образом дёргается код старой функции

leov19:01:23

это чем-то можно объяснить?

leov19:01:46

или без кода сложно понять что происходит

misha19:01:09

попробуй вирджил

leov19:01:54

господи. я бы наоборот избавился от джавы раз и навсегда 😃

leov19:01:42

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

leov19:01:58

я вообще не уверен что вирджил будет работать под андроид..)

leov19:01:42

под андроидом какая-то полная ж. единороги, волшебство и радуга.

dottedmag20:01:06

@leov Какой тип у объекта, который нужно пихать джаве?

dottedmag20:01:14

Runnable какой-нибудь?

dottedmag20:01:20

@leov Происходит следующее, если я правильно расшифровал подземный стук - когда ты даёшь джаве свою функцию, ты даёшь ей IFn, который значение функции. Когда ты обновляешь функцию - ты меняешь состояние Var, в котором начинает лежать новое значение.

dottedmag20:01:30

Т.е. это то же самое, что сначала сказать (def myvar 42), потом передать myvar в джаву, а потом удивляться, что после (alter-var-root #'myvar 43) джава продолжает использовать старое значение.

dottedmag20:01:10

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

dottedmag20:01:56

@leov чтобы этого избежать, достаточно передавать в джаву вместо myfun#(myfun %), который каждый раз будет разыменовывать Var, а не в момент засовывания значения в листенер.

dottedmag20:01:01

В кложе всё – неизменные данные, и функции тоже 🙂 При переопределении функции меняется только Var.

dottedmag20:01:07

@leov А ещё Var реализует IFn, поэтому вместо анонимной функции в джаву можно передать #'myfun

mike_ananev20:01:11

@misha по поводу ссылки на твиттер. там один чувак спросил: what's so complex about abstract proxy bean factory factories? мне показалось это смешным, т.к. ответ: Все!

mike_ananev20:01:48

современное OOP таких монстров породило вида "ООП ради ООП", что отношения бизнеса и программистов похожи на отношения светской власти и шаманов. Шаманы просят дофига ресурсов ибо ООП код пухнет и требует больше помощников и ресурсов для шамана. Бедный бизнес платит за это. По моему мнению Рич вернул программирование на ту дорогу, с которой когда-то свернули. То есть признал шаманов шарлатанами и сказал "в жопу тайп корректнес - даешь business корретный код в массы".

mike_ananev20:01:41

в общем кложа екзистенциальная угроза религиозным фанатикам ООП.

mike_ananev20:01:43

ну не получается у них переписать сервис из 2000 строк на кложе меньше чем 6000 на скала и не менее чем в 2 раза дольше по времени.

dottedmag20:01:20

@mike1452 Не для всех компаний это проблема. Но да, это отличное конкурентное преимущество.

dottedmag20:01:08

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

dottedmag20:01:03

Даже Cognitect с самого начала был распределённым, а они-то могут себе позволить снять сливки 🙂

artemyarulin22:01:53

хм, а я могу как-нить из мапы достать значения по ключу но не fully qualified?

(def x #:person {:id 1 :identity 42 :name "Sam" :on? true :age 23 :friend [:person/id 2]})
(def y #:company {:id 1 :name "Co#1" :team [[:person/id 1] [:person/id 2]]})

;; Нужно достать :id, префикс для ключей допустим я не знаю, можно вот костыльно, ничо лучше нету?
(filter #(= "id" (-> % first name)) (seq x)) ;;> ([:person/id 1])
(filter #(= "id" (-> % first name)) (seq y)) ;;> ([:company/id 1])