This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2015-12-03
Channels
- # admin-announcements (8)
- # beginners (99)
- # boot (60)
- # cider (44)
- # cljs-dev (47)
- # cljsrn (68)
- # clojure (223)
- # clojure-art (1)
- # clojure-russia (190)
- # clojure-sg (9)
- # clojure-uk (2)
- # clojurecup (1)
- # clojurescript (59)
- # clojurex (3)
- # core-async (43)
- # core-typed (2)
- # cursive (18)
- # datavis (7)
- # datomic (16)
- # events (2)
- # funcool (3)
- # hoplon (3)
- # jobs (1)
- # lein-figwheel (10)
- # leiningen (6)
- # off-topic (1)
- # om (123)
- # onyx (57)
- # parinfer (16)
- # portland-or (2)
- # random (1)
- # re-frame (4)
- # reagent (7)
- # remote-jobs (1)
- # spacemacs (12)
есть такой код - http://pastebin.com/HVvHpNYZ
и в чем именно проблема?
а в чем именно проблема с макросом?
(defmacro generate-lex
[lex-name en ru]
(let [ru-name (clojure.string/join [lex-name "-ru"])
...
`(defparser ~ru-name [] (let->> ...)
...
)))
проблема - преобразовать строку символов, например "var" в парсер вида (let->> [v1 (choice (char \v) (char \V)) v2 (choice (char \a) (char \A)) v3 (choice (char \r) (char \r)) ] (always {:value (clojure.string/join [v1 v2 v3])})
Не понимаю (не знаю) как сдлеать следующее - по строке сгенерировать список, этот список правильно поставить в парсер, объявить 3 парсера в одном макросе
то что я вижу в снипете это попытка сгенерировать строку с кодом. Строку затем можно превратить в код при помощи (eval (read-string “(my clojure code”)))
, но по-моему лучше все таки написать макрос, который будет генерировать нужный код..
ognivo: явашный тредпул очень качественный. Если нужно тонкое управление, проще над ним удобную обёртку просто сделать.
Я ещё выше сказал, конкурирующие процессы последовательно уложить - это агент справится, даже думать не надо.
С макросом код проще не станет, иногда даже наоборот. Некоторые вытаскивают в макрос потому что он компайл-тайм, то есть во время компиляции развернётся и просчитается.
макросы нельзя передать как параметр в функции, нельзя сохранить в переменной, вообще нихрена с ним нельзя сделать.
Макросы надо применять только когда обычная функция не канает. Ну или для фана, чтоб пацанам показать
(defparser lxmProcedure-ru [] (let->> [start (lineno) var-1 (choice (char \п) (char \П)) var-2 (choice (char \р) (char \Р)) var-3 (choice (char \о) (char \О)) var-4 (choice (char \ц) (char \Ц)) var-5 (choice (char \е) (char \Е)) var-6 (choice (char \д) (char \Д)) var-7 (choice (char \у) (char \У)) var-8 (choice (char \р) (char \Р)) var-9 (choice (char \а) (char \А)) ] (always {:lang :ru :value (clojure.string/join [ var-1 var-2 var-3 var-4 var-5 var-6 var-7 var-8 var-9]) :pos start}))) (defparser lxmProcedure-en [] (let->> [start (lineno) var-1 (choice (char \p) (char \P)) var-2 (choice (char \r) (char \R)) var-3 (choice (char \o) (char \O)) var-4 (choice (char \c) (char \C)) var-5 (choice (char \e) (char \E)) var-6 (choice (char \d) (char \D)) var-7 (choice (char \u) (char \U)) var-8 (choice (char \r) (char \R)) var-9 (choice (char \e) (char \E)) ] (always {:lang :en :value (clojure.string/join [ var-1 var-2 var-3 var-4 var-5 var-6 var-7 var-8 var-9]) :pos start}))) (defparser lxmProcedure [] (let->> [start (lineno) v (choice (lxmProcedure-ru) (lxmProcedure-en))] (always {:type :lxmProcedure :value v :pos start})))
и не читаемо и смысла никакого... Проще бы выглядело (generate-lex "lxmProcedure" "procedure" "процедура")
итак, есть такое -
(defmacro generate-lex
[lex-name en ru]
(let [ru-name (clojure.string/join [lex-name "-ru"])
ru-parser (map (fn [x y] [(clojure.string/join ["var" ~y "#"])
(choice (char ~(clojure.string/lower-case x))
(char ~(clojure.string/upper-case x)))]) ru (take (count ru) (int-seq)))]
`(defparser ~ru-name []
(let->> [~ru-parser]
(always {:result :test})
))))
при раскрытии
loxy.core> (macroexpand-1 '(generate-lex lxmOR "or" "или"))
(the.parsatron/defparser "lxmOR-ru" [] (the.parsatron/let->> [(["var1#" (the.parsatron/choice (clojure.core/char "и") (clojure.core/char "И"))] ["var2#" (the.parsatron/choice (clojure.core/char "л") (clojure.core/char "Л"))] ["var3#" (the.parsatron/choice (clojure.core/char "и") (clojure.core/char "И"))])] (the.parsatron/always {:result :test}))
как поубирать строки у var и сделать из (char "и") char (\и)
а если для фана то конечно гогого. Пользуюсь случаем - мне стало все понятно после прочтения вот этой книженции http://www.amazon.com/Mastering-Clojure-Macros-Cleaner-Smarter/dp/1941222226
вот так в отформтированном фиде выглядит - (defparser lxmProcedure-ru [] (let->> [start (lineno) var-1 (choice (char \п) (char \П)) var-2 (choice (char \р) (char \Р)) var-3 (choice (char \о) (char \О)) var-4 (choice (char \ц) (char \Ц)) var-5 (choice (char \е) (char \Е)) var-6 (choice (char \д) (char \Д)) var-7 (choice (char \у) (char \У)) var-8 (choice (char \р) (char \Р)) var-9 (choice (char \а) (char \А)) ] (always {:lang :ru :value (clojure.string/join [ var-1 var-2 var-3 var-4 var-5 var-6 var-7 var-8 var-9]) :pos start}))) (defparser lxmProcedure-en [] (let->> [start (lineno) var-1 (choice (char \p) (char \P)) var-2 (choice (char \r) (char \R)) var-3 (choice (char \o) (char \O)) var-4 (choice (char \c) (char \C)) var-5 (choice (char \e) (char \E)) var-6 (choice (char \d) (char \D)) var-7 (choice (char \u) (char \U)) var-8 (choice (char \r) (char \R)) var-9 (choice (char \e) (char \E))] (always {:lang :en :value (clojure.string/join [ var-1 var-2 var-3 var-4 var-5 var-6 var-7 var-8 var-9]) :pos start}))) (defparser lxmProcedure [] (let->> [start (lineno) v (choice (lxmProcedure-ru) (lxmProcedure-en))] (always {:type :lxmProcedure :value v :pos start})))
у меня уже так в макросе -
(defmacro generate-lex
[lex-name en ru]
(let [ru-name (make-sym (clojure.string/join [lex-name "-ru"]))
ru-parser (map (fn [x y] [(clojure.string/join ["var" ~y "#"])
(choice (char ~(clojure.string/lower-case x))
(char ~(clojure.string/upper-case x)))]) ru (take (count ru) (int-seq)))]
`(defparser ~ru-name []
(let->> [~ru-parser]
(always {:result :test})
))))
loxy.core> (macroexpand-1 '(generate-lex lxmOR "or" "или")) (the.parsatron/defparser lxmOR-ru [] (the.parsatron/let->> [(["var1#" (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))] ["var2#" (the.parsatron/choice (clojure.core/char \л) (clojure.core/char \Л))] ["var3#" (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))])] (the.parsatron/always {:result :test})))
@ponimas не думаю, но его можно расширить через мультиметода - https://github.com/niquola/clj-pg/blob/master/src/clj_pg/honey.clj
смотри - сейчас макрос такой - (defmacro generate-lex [lex-name en ru] (let [ru-name (make-sym (clojure.string/join [lex-name "-ru"])) ru-parser (into [] (map (fn [x y] [(make-sym (clojure.string/join ["var" `~y "#"])) `(choice (char ~((into [] (clojure.string/lower-case x)) 0)) (char ~((into [] (clojure.string/upper-case x)) 0)))]) ru (take (count ru) (int-seq))))] `(defparser ~ru-name [] (let->> ~ru-parser (always {:result :test}) )))) и вывод - loxy.core> (macroexpand-1 '(generate-lex lxmOR "or" "или")) (the.parsatron/defparser lxmOR-ru [] (the.parsatron/let->> [[var1# (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))] [var2# (the.parsatron/choice (clojure.core/char \л) (clojure.core/char \Л))] [var3# (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))]] (the.parsatron/always {:result :test}))) как видно для (let->> вывод неверный. Есть let->> [[var1# ( а надо let->> [var1# (
1. ~@ru-parser, 2. для имени вара нужно использовать gensym, 3. Если строка, то её надо в символ преобразовать.
ну типа пишешь ты макрос типа (defmacro [& body] `(let [foo# (computation)] (prn foo#) ~@body)))
и оно тебе обеспечивает, что у тебя имена биндингов не пересекутся с тем, что внутри body
> А ридер макро в кложуре, вроде, отстойный какой-то. нет "ридера макро". есть просто ридер
и так. есть generate-lex. он делает две вещи: формирует собственно неких лексер и добавляет несколько def-ов. первая часть решается без макросов и лишь вторая — это дело макроса
(macroexpand-1 '(generate-lex lxmOR "or" "или")) (the.parsatron/defparser lxmOR-ru [] (the.parsatron/let->> [[var12250 (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))] [var12251 (the.parsatron/choice (clojure.core/char \л) (clojure.core/char \Л))] [var12252 (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))]] (the.parsatron/always {:result :test})))
(defmacro generate-lex [lex-name en ru] (let [ru-name (make-sym (clojure.string/join [lex-name "-ru"])) ru-parser (into [] (map (fn [x y] [ (gensym "var") `(choice (char ~((into [] (clojure.string/lower-case x)) 0)) (char ~((into [] (clojure.string/upper-case x)) 0)))]) ru (take (count ru) (int-seq))))] `(defparser ~ru-name [] (let->> ~ru-parser (always {:result :test}) ))))
(the.parsatron/defparser \l \x \m \O \R \- \r \u [] (the.parsatron/let->> [[var12175 (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))] [var12176 (the.parsatron/choice (clojure.core/char \л) (clojure.core/char \Л))] [var12177 (the.parsatron/choice (clojure.core/char \и) (clojure.core/char \И))]] (the.parsatron/always {:result :test}))) - тут имя как последовательность char
wwall: в общем если хочется совета, то это нужно делать не так. давай приведем все это к фунции, которая на вход принимает word и возвращает сгенерированную функцию. и приведем к хорошему коду. это не тот кейс на котором нужно "учить макросы". макросы здесь можно заюзать для создания вара.
flatten
вопрос - никто не делала optimistic UI вообще и в om-next в частности? На самом деле Ом не причем, просто не пойму как вообще обрабатывать ошибки например: - Пользователь создал новую сущность, я в UI ее показал как будто создана, отправил на сервер сообщение для создания - Пользователь поменял эту сущность. Я это благополучно сделал в UI, отправил на сервер Первое сообщение вернулось с ошибкой и чо теперь делать? Ну т.е. я счас на коленке придумаю конечно какой workaround именно для этого кейса, но может есть какие стратегии/паттерны для общего случая?
"отправил на сервер сообщение для создания" а что в этом случае отправляется на сервер? я правильно понимаю что данные о состоянии элемента?
и в ответ должно прийти новое состояние элемента?
@delaguardo: вся сущность целиком, либо как минимум то что должно сохранить в базе
есть такое
(defmacro deftoken
[lex-name en ru]
(let [
ru-name (make-sym (clojure.string/join [lex-name "-ru"]))
en-name (make-sym (clojure.string/join [lex-name "-en"]))
ru-parser-data (into [] (map (fn [x y] [ (gensym "ru-var")
`(choice
(char ~((into [] (clojure.string/lower-case x)) 0))
(char ~((into [] (clojure.string/upper-case x)) 0)))]) ru (take (count ru) (int-seq))))
ru-parser-binding (into [] (apply concat ru-parser-data))
ru-list-binding (into [] (map (fn [[x y]] x)) ru-parser-data )
en-parser-data (into [] (map (fn [x y] [ (gensym "en-var")
`(choice
(char ~((into [] (clojure.string/lower-case x)) 0))
(char ~((into [] (clojure.string/upper-case x)) 0)))]) en (take (count en) (int-seq))))
en-parser-binding (into [] (apply concat en-parser-data))
en-list-binding (into [] (map (fn [[x y]] x)) en-parser-data )]
@(`(defparser ~ru-name []
(let->> ~ru-parser-binding
(always {:lang :ru :value (clojure.string/join ~ru-list-binding) })))
`(defparser ~en-name []
(let->> ~en-parser-binding
(always {:lang :en :value (clojure.string/join ~ru-list-binding) })))
`(defparser ~lex-name []
(let->> [start (lineno)
v# (choice (ru-name) (en-name))]
(always {:pos :start :value v#}))))))
дада, это один кейс всего, может что угодно быть. Надо посмотреть как делать синхронизацию вообще - это походу частный случай ее
@artemyarulin вывести экран для merge ;)
ну до мержа точно доводить не хочу, хотя на гит смотрел да:) Пока вывел несколько правил когда optimistic update запрещен: - сущность тока создана в pending state, редактировать ее нельзя - удаление через is-deleted чтобы можно было вернуть назад легко - если сущность в pending state - связанные с ней менять тоже нельзя т.е. идея конфликт исключить вообще
точнее не так - конфликт будет, но не будет проблем когда одну транзакцию надо отменить, а на основе нее уже 100 других
если простыми словами: один пользователь отредактировал - другой удалил - кто из них прав? система не знает, знает только кто первый ( и то не всегда). По честному надо их обоих сконнектить - чтоб они к консенсусу пришли. Т.е. открывается групповой чатик и merge инструмент и они сами решают
ну у меня не совсем прям real-time collaboration. Чисто client-server, но клиент может сидеть с нескольких машин/девайсов.
У меня емайл клиент по сути - пометил сообщения как прочитанное, другое удалил, третье написал, на четвертое ответил, в принципе для этого стандартного кейса все будет выглядеть как optimistic. А вот когда написал сообщение, положил в черновики и решил сразу ее отредактировать то - низя да, ибо еще не пришел confirm from server.
Ну тут я уже углубляюсь в логику своего приложения, т.е. да ответ тут верный дали - depends on app
@artemyarulin: исключить конфликты наверняка можно только CRDT, но тема любопытная
@nicola: > открывается групповой чатик и merge инструмент и они сами решают вот я пока склоняюсь к Колиной позиции, хотя наш фронтенд жутко против, но я не на этом проекте с синхронизацией и оффлайном...
@razum2um: во спасибо, почитаю. Тема и впрям интересная, странно что быстрый гуглешь ничего не дал - вроде optimistic updates тема счас популярная
CRDT часто ничего не решает. ну т.е. вот если вы удалили разные элементы списка какого-нибудь — ок, но это и без crdt можно решить часто. А тема типа "я удалил, он отредактировал" не особо решается. Совместное редактирование текста, на сколько мне известно, тоже лучше решать не через CRDT.
Эта тема вообще как-то так связывает тему распределенных баз данных и построение GUI (казалось бы!), что не думаю, что ее хоть кто-то научился элегантно решать в более-менее общем случае
просто Нолен в презентации говорил что ом-некст, траляля, история, optimistic updates, сломалось - откатил и я что-то прям ожидал ОМГ все из коробки
@prepor: ну так @artemyarulin правильно заметил, “удалил” не должно быть, есть “отредактировал +deleted=1”
и выступление Нолена кстати радует, чувствуется, что он туда смотрит и хочет гордиться передовым построением ui
razum2um: в целом да. но при наличии единого транзактора crtd как то не особо в касу. ты и так можешь сообщить клиенту последнее валидное состояние и только оно будет валидным, не важно что там думает клиент
один транзактор не масштабируется, да и “забить на клиента и принудительно его сбрасывать” тоже не айс 😕
не масштабируется, но се ля ви. к тому же распределенный эффективный транзактор тоже не фантастика
кстати пока гуглил нашел как метеор эту проблему “решил” - локальная транзакция апплаиться на локальный сторедж во фронте. Уходит запрос на сервак, приходит ответ от него - откатывается локальная транзакция и апплаиться уже данные с сервака ибо “сервак всегда прав”(с) Совсем не ах, зато из коробки
и так, наверно, жить все же удобнее и понятнее чем в eventually consisten мире crtd-структур. но я хз
ну операция, я хз вообще про метеор - магия сплошная, а такого не люблю
ну т.е. я просто про подход - пока гуглил просто наткнулся на “фсе фреймворки гавно и тока один метеор цветет ибо optimistic update везде и ничего не надо делать"
@artemyarulin: кстати, наши заюзали вот это https://github.com/gritzko/swarm
спасибо, посмотрю чо там внутрях