Вот есть такой классический способ в клоуже:
(-> [[1 2] [:a :b]] (get 1) first) ; => :a
сдесь (get 1)
вместо second
просто ради примера использования функции с более чем одним параметром)
А мне нужен какой то такой вдобавок:
((fn-> (get 1) first) [[1 2] [:a :b]]) ; => :a
(что бы такую новосозданную функцию можно было куда то передать, и позже вызвать с нужным аргументом)
реализация проста (может я правда что то не учел...):
(defmacro fn-> [& forms]
`(fn [x#]
(-> x# ~@forms)))
Вопрос - есть идеи лучшего/более-очевидного имени?
Оригинально я создал такую штуку c другим более очевидным именем:
(defmacro comp-> [& fns]
`(comp ~@(reverse fns)))
ну или так (если без макросов):
(defn comp-> [& fns]
(apply comp (reverse fns)))
она удобна в тех случаях когда все функции, которые нужно скомпоновать в "натуральном порядке а не навыворот", - принимают только один параметр
также такая реализация будет более удобной в случае использования анонимных функций:
((comp-> #(get % 1) first) [[1 2] [:a :b]]) ; => :a
против более “скобастого":
((fn-> (#(get % 1)) first) [[1 2] [:a :b]]) ; => :a
Это я веду к тому что может удобно иметь оба варианта... Как тогда оба эти назвать?
Раньше я думал что comp-> это очевидно для простого "reversed comp", потому что стрелочка указывает на то что это тот же комп, но "составленный" последовательно с функций, поэтому и порядок изменится...
Теперь я понимаю, что у "очевидности" могут ноги расти и из того, что стрелочка - это таки всем уже известный макрос, и приставка comp - просто говорит о том что мы компонуем функцию из того что и раньше можно было в стрелочку-макрос запихнуть... Но при той реализации для comp-> что я показал выше - это не верно... значит таки лучше реализовать comp-> как:
(defmacro comp-> [& forms]
`(fn [x#]
(-> x# ~@forms)))
но тогда какое имя придумать для обычного "reversed comp"? - может просто rcomp
какой то? другие идеи? 🙂 может есть другие "символы" которые могут быть очевидны как "инверсия"…
ну или может таки забить нафиг на
удобность
(defn comp-> [& fns]
(apply comp (reverse fns)))
((comp-> #(get % 1) first) [[1 2] [:a :b]]) ; => :a
и оставить реализацию более скобастую и с ней уже и мириться:
(defmacro comp-> [& forms]
`(fn [x#]
(-> x# ~@forms)))
((comp-> (#(get % 1)) first) [[1 2] [:a :b]]) ; => :a
?
типа как оставить одну реализацию пускай иногда менее удобную - это более KISS...
Например что бы в ring+compojure писать более стрейтфорвард и потому KISS код:
(defroutes app-routes
(GET "/" [] handlers/show-main)
(GET "/login" [] handlers/show-login)
(POST "/login" [] (comp-> params-request
keyword-params-request
handlers/do-login)))
(def app
(-> app-routes
(wrap-authentication auth/backend)
wrap-session))
вместо:
(defroutes app-routes
(GET "/" [] handlers/show-main)
(GET "/login" [] handlers/show-login)
(POST "/login" [] (comp-> params-request
keyword-params-request
handlers/do-login)))
(def app
(-> app-routes
(wrap-authentication auth/backend)
wrap-keyword-params
wrap-params
wrap-session))
Как видно в последнем варианте порядок wrap-keyword-params wrap-params менее натуральный, потому что навыворот. Плюс эти миддлвары навешаны на все роуты, несмотря на то что нужны только для одного.
Как то мне от всей этой обычной лапши в кольце мидлваров - не по себе. Выглядит как одна цепочка, в которой на самом деле есть куски маршрута которые нужно читать "по часовой стрелке/т.е. вниз" (обработчики респонса), есть "какие против/т.е. вверх" (обработчики реквеста) а есть какие "и туда и обратно" (как с wrap-session). И при этом все это нужно держать в голове, и не дай бог случайно не поменять порядок, а то все накроется медным тазом.
Мне прям сильно странно как это так люди в мире клоуже - бьют поклоны ring-у, и терпят такое извиняюсь безобразие:), прям "one noodles ring to confuse rule them all"
В идеале хотелось бы чего то такого (более однозначного, очевидного, и менее склонного к ошибкам):
(defroutes app-routes
(ALL [] (comp-> session-request
(authentication-request auth/backend))
(GET "/" [] handlers/show-main)
(GET "/login" [] handlers/show-login)
(POST "/login" [] (comp-> params-request
keyword-params-request
handlers/do-login)))
(def app
(-> app-routes
wrap-session-response))
Но для этого уже прийдется подшаманить в самом компоуже...
Кстати, можно конечно и без comp-> обойтись в этом случае:
(defroutes app-routes
(ALL req (-> req session-request
(authentication-request auth/backend))
(GET "/" [] handlers/show-main)
(GET "/login" [] handlers/show-login)
(POST "/login" req (-> req params-request
keyword-params-request
handlers/do-login)))
(def app
(-> app-routes
wrap-session-response))
Хотя мне кажется с comp-> более лаконично и читабельно, плюс насколько я видел код с компоуже - более идиоматично следующее
(GET "/" [] handlers/show-main)
чем
(GET "/" req handlers/show-main)
то есть не упоминать req там где можно без этого обойтись... хотя может это я уже и выдумываю...