This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-08-30
Channels
- # aws (2)
- # beginners (139)
- # boot (9)
- # cider (1)
- # clara (2)
- # cljs-dev (35)
- # cljsrn (3)
- # clojure (112)
- # clojure-dusseldorf (9)
- # clojure-greece (6)
- # clojure-italy (13)
- # clojure-russia (160)
- # clojure-seattle-old (1)
- # clojure-uk (79)
- # clojurescript (85)
- # clojutre (1)
- # community-development (11)
- # core-async (32)
- # cryogen (2)
- # cursive (5)
- # data-science (16)
- # datomic (2)
- # events (1)
- # fulcro (29)
- # funcool (1)
- # graphql (4)
- # immutant (5)
- # instaparse (20)
- # jobs (2)
- # juxt (6)
- # leiningen (11)
- # luminus (21)
- # lumo (1)
- # off-topic (7)
- # onyx (20)
- # parinfer (33)
- # pedestal (4)
- # re-frame (41)
- # reagent (34)
- # ring-swagger (14)
- # rum (5)
- # spacemacs (9)
- # specter (11)
- # sql (14)
- # test-check (3)
- # yada (20)
есть еще вот такое: https://github.com/Engelberg/better-cond/tree/v2.0.1
(b/defnc call [ctx params]
:let [err (or
(check-logged-out ctx)
(check-params params)
(check-registered ctx params))]
(some? err) [nil err]
:let [user-id (create-user ctx params)]
:do (sign-in ctx user-id)
:let [res {:user-id user-id}]
[res nil])
а какова мотивация? кода ведь не меньше, читается не лучше... чтобы людям, незнакомым с этой либой (better-cond), было сложнее понять исходник?
нет. но со всякими :let и :do структура получается в стиле императивного кода (типо if (x) { return null; })
но... конечно такой болшой и сложный макрос -- это не настолько круто, как отдельная либа 🙂
https://web.archive.org/web/20160531054959/http://clj-me.cgrand.net/2011/06/17/a-flatter-cond/
я не знаю) но направление правильное я не хочу разбираться сейчас в этом и ломать мозг рекурсией макросов в том же better-cond тоже рекурсивный макрос, но я его не вижу, он внутри библиотеки в better-cond оно протестировано и используется
если better-cond действительно можно выкинуть и зменить на макрос в 2 строчки, то это круто
поэтому я не вижу реального профита от такой записи -- ну мы не лесенкой пишем, а "линейно"... но для этого нам нужно "ломать мозг рекурсией макросов" (хотя макрос наитривиальнейший на самом то деле)
благо тогда и новые фичи проще добавлять (доп ветки в if-ы проще будет вставлять, цикл можно будет запилить каокй и т.п.)
@kuzmin_m на самом деле лесенка меня тоже цепляет, но это всего лишь напоминание разбить ф-ю на части
(defn ctx-errors [ctx params]
(or
(check-logged-out ctx)
(check-params params)
(check-registered ctx params)))
(defn create-user-and-sign-in [ctx params] ;; TODO better name
(let [user-id (create-user ctx params)]
(sign-in ctx user-id)
{:user-id user-id}))
(defn call [ctx params]
(if-let [err (ctx-errors ctx params)]
[err nil]
[nil (create-user-and-sign-in ctx params)]))
(->> (create-user ctx params)
(sign-in ctx)
(assoc {} :user-id))
кстати да, вообще @kuzmin_m немного смущает, что sign-in это сайд-эффект. как насчет между флоу меняться контекстом и в изначальной ф-и это будет не сайд-эффект, а leaf нода в логике. тогда и тредмакросы будут красивы
и я тут тоже думал, что лучше юзать как аналог Either, и мне кажется лучше это хеш типа {:err nil :res nil}
чем вектор.
потому что можно делать (-> {:err nil :res nil} fn-whoch-assocs-errors fn-which-shortcuts-on-error-via-macros-and-assocs-res)
да, clojure напоминает разбить функцию на части но в этом случае жесть какая-то у меня есть задача зарегать пользователя она состоит из 2х шагов: проверить данные 3мя проверками, создать пользователя, залогинить его все на одном уровне абстракции т.е. я не говорю как я регаю, как я проверяю и как я логиню по этому разбивать еще - очень странно для меня мой пример очень простой, там всего одна ступенька в лесенке а что делать, если их больше?
def move_link_to_packet_for_replaced_race(race)
return if data.replaced_order_uid.blank?
replaced_race = Race.find_by(uid: data.replaced_order_uid)
return if replaced_race.blank?
return if replaced_race.packet.blank?
replaced_race.packet_attached_race.update! race: race
end
@razum2um https://github.com/darkleaf/publicator/blob/master/src/publicator/interactors/user/register.clj вот полный код
придумай контекст для этого флоу, можно больше чем выше, а прям {:err nil :res nil :step1 nil :step2 nil}
и тредмакросом его прогоняй по каждому шагу
эм. мне кажется clojure-way это сделать типа такого
(defn pipeline []
(macros-aka-some->-but-for-context->
empty-pipeline-context
(step1 ctx)
(step2 params)))
зачем мне слова заказчика в голове переводить в "функциональный" стиль через передачу контекста через -> ?
@anjensan кстати, хорошее замечание. я думаю идеально, если этот тредмакрос выдает агрументы и инструкции для вызова сайдэффектом. redux-saga типа. ну и теститься будет на ура
да. это неплохой, красивый вариант... хотя конкретно тут (залогинить юзера) имхо идет процесс высасывания проблемы из пальца
"я не хочу писать (if (> a b) (inc a) (dec b)), я хочу "(if (> a b) (return (inc a)) (dec b)"
ну до этого на чем писал? я тоже из рубей и мне тоже не хватало(ет?) раннего ретурна, но для меня это решается cond->/some-> & nil pinning или аналогом как выше. ну и более детальной разбивкой
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
int retval = SUCCESS;
if (someCondition)
{
if (name != null && name != "")
{
if (value != 0)
{
if (perms.allow(name)
{
// Do Something
}
else
{
reval = PERM_DENY;
}
}
else
{
retval = BAD_VALUE;
}
}
else
{
retval = BAD_NAME;
}
}
else
{
retval = BAD_COND;
}
return retval;
}
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
if (!someCondition)
return BAD_COND;
if (name == null || name == "")
return BAD_NAME;
if (value == 0)
return BAD_VALUE;
if (!perms.allow(name))
return PERM_DENY;
// Do something
return SUCCESS;
}
Через binding
может “середина” означает, что процесса по факту 2? т.е. руби функцию тоже бы отрефакторить, как думаешь?
def move_link_to_packet_for_replaced_race(race)
return if data.replaced_order_uid.blank?
replaced_race = Race.find_by(uid: data.replaced_order_uid)
return if replaced_race.blank?
return if replaced_race.packet.blank?
replaced_race.packet_attached_race.update! race: race
end
ну это реально больно и мучительно. даже с руби на какую джаву будет больно переписывать
@kuzmin_m что думаешь если так
def move_link_to_packet_for_replaced_race(race)
return if data.replaced_order_uid.blank?
RaceWrap.new(Race.find_by(uid: data.replaced_order_uid)).move
end
class RaceWrap # some process class wrapping race maybe extended by some functionality further
attr_reader :race
def initialize(race)
@race = race
end
def can_packet_attached_race?
race.present? && race.packet.present?
end
def update
race.packet_attached_race.update! race: race
end
def move
update if can_packet_attached_race?
end
end
конечно “плодить сущности” плохо и правильно назвать враппер надо, но по мне в инзачальной краткости можно потом утонуть. ну и тестится здесь проще же?так а зачем трогать рубиновый код? он идиоматичен для руби? если да (подозреваю что да, но python-er а не рубист), то и ок
я пытался доказать, что Ъ с т.з. расширяемости и тестирования это все-таки разбить. даже на руби
для clojure такой код не идеоматичен (всякие макросы с (return)) -- зачем впихнуть невпихуемое ?
было
def move_link_to_packet_for_replaced_race(race)
return if data.replaced_order_uid.blank?
replaced_race = Race.find_by(uid: data.replaced_order_uid)
return if replaced_race.blank?
return if replaced_race.packet.blank?
replaced_race.packet_attached_race.update! race: race
end
def move_link_to_packet_for_replaced_race(race)
return if data.replaced_order_uid.blank?
RaceWrap.new(Race.find_by(uid: data.replaced_order_uid)).move
end
class RaceWrap # some process class wrapping race maybe extended by some functionality further
attr_reader :race
def initialize(race)
@race = race
end
def can_packet_attached_race?
race.present? && race.packet.present?
end
def update
race.packet_attached_race.update! race: race
end
def move
update if can_packet_attached_race?
end
end
@anjensan не, метод move_link_to_packet_for_replaced_race правда не плох (за исключением имени), но я бы сказал, что тестируется хуже, а главное, что этот класс-враппер это же классика “extract class”. т.е. возможно того изначального метода вообще быть не должно, а враппер быть с нормальной семантикой процессом
@kuzmin_m ты просто глянь на реализацию этого letr... не дай бог что пойдет не так (а я уверен что там куча корнер кейсов вылезет), ты будешь 100 раз проклинать что решил юзать такую магию
вот часть реализации - там идет полноценный walk по всему коду, что ты написал... это черевато
просто если ты пробуешь, учишь, играешься с clojure - имеет смысл для начала помучаться и юзать только clojure.core
перетерпеть, може чутка поломать мозг... а уже потом всякие вывернутые макросы юзать