This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-02-13
Channels
- # bangalore-clj (1)
- # beginners (29)
- # boot (13)
- # braveandtrue (5)
- # clara (5)
- # cljs-dev (42)
- # cljsrn (7)
- # clojure (55)
- # clojure-france (46)
- # clojure-nl (11)
- # clojure-portugal (1)
- # clojure-russia (268)
- # clojure-spec (26)
- # clojure-uk (32)
- # clojurescript (173)
- # clr (2)
- # core-async (46)
- # cursive (22)
- # datomic (33)
- # devcards (1)
- # emacs (5)
- # events (8)
- # figwheel (2)
- # flambo (4)
- # instaparse (8)
- # jobs (11)
- # klipse (46)
- # lein-figwheel (3)
- # london-clojurians (2)
- # nrepl (1)
- # off-topic (29)
- # om (4)
- # om-next (8)
- # pedestal (3)
- # rdf (4)
- # re-frame (51)
- # reagent (104)
- # remote-jobs (1)
- # rum (4)
- # schema (2)
- # specter (19)
- # untangled (16)
- # vim (52)
т.е. ты хочешь тестировать в изоляции и мокать IO или ты хочешь реальное IO делать в тестах?
Я решил примерно так у себя
(defn get-customer-fn [io id]
(-> io id do-other-business-logic))
(def get-customer (partial get-customer-fn io/http))
Потом соответсвенно можно покрыть внутреннюю get-customer-fn как угодно. Но это не совсем ответ на твой вопрос, это скорее как на него не отвечать 🙂у меня новый проект и пока маленький + мобайл т.е. не так много стейта типа всяких db-connection-pull и прочих традиционных на беке, поэтому данный паттерн идиален при таком масштабе
ну ага, есть with-чото-там, сек
(ns http)
(defn post [url]
{:body "Hello world"})
(ns app
(:require [clojure.test :refer [deftest is run-tests]]))
(deftest is-a-macro
(with-redefs [http/post (fn [url] {:body "Goodbye world"})]
(is (= {:body "Goodbye world"} (http/post "")))))
(run-tests) ;; test is passing
но вроде я как-то читал что это плохя практика нынче, хз, я не юзал никогда
или там было про минусы with-bindings точно не помню, я не юзал ни одни из них, не могу сказать
может тебе начать с чистых функций сначала?
не, про чистые функции мне более-менее понятно, я хочу понять, как использовть спеки на реальном коде
понял да, неа, не могу сказать. Спроси еще в #clojure-spec, там народу поболее
как найдешь ответи запости сюда тоже
ну а как сейчас (без спеки) ты тестируешь?
ааа оо, сек, там есть функция в спеках
While both instrument (for enabling :args checking) and check (for generating tests of a function) are useful tools, they can be combined to provide even deeper levels of test coverage.
instrument takes a number of options for changing the behavior of instrumented functions, including support for swapping in alternate (narrower) specs, stubbing functions (by using the :ret spec to generate results), or replacing functions with an alternate implementation.
(stest/instrument `invoke-service {:stub #{`invoke-service}})
Combining check and instrument параграф в https://clojure.org/guides/specда, чота я затупил, забыл что оно есть, как раз для этого
вот тут статейка от Сиерры по поводу with-bindings Friends Don’t Let Friends Use Dynamic Scope
https://stuartsierra.com/2013/03/29/perils-of-dynamic-scope
про with-redefs я хз, к стыду не знаю как ни один из них работает 🙂
слу, а у тебя есть свои репозитории на гитхабе со спеками? я хотел посмотреть, как структурировать код
только если очень-очень нужно чё-там там подменить, да и то не припомню, когда такое было
Да блин, я тоже искал и спрашивал в канале спеки, нету ответа, а светлолицый молчит 🙂 Я думаю в зависимости от задачи 1) Кто-то спеку считает частью тестов и кладет там 2) Кто-то считает спеку частью кода и кладет рядом с кодом 3) Кто-то считает спеку частью кода но кладет отдельно ибо она опциональна + спека мгогословна + у меня часто в спеке тестовые данные аля
(s/def ::get-customer-response-success #{”<xml>.... soap-response-1 ....</xml>” ”<xml>.... soap-response-2 ....</xml>”})
(s/def ::get-customer-response-error #{”<xml>.... soap-err-response-1 ....</xml>” ”<xml>.... soap-err-response-2 ....</xml>”})
(s/def ::get-customer-response-any (s/or :success ::get-customer-response-success :fail ::get-customer-response-error”))
я выбрал пока №3
примеров нету сори, приватный репо, но идею я показал
а у меня, ты знаешь, такая вот мысль есть: сильно глубоко специфицируют тесты, прям уж очень-очень зависимые типы получаются
я не уверен кста что это вообще правильно спеку с моками и тестовыми данными мешать, но мне просто очень дорого интеграционные тесты гонять и со спекой вроде норм получается
ну тогда может просто рядом с кодом? там обычно будет однострочник поэтому не слишком шумно
ну т.е. чтобы во время работы функции она проверяла входные и выходные параметры на основе fdef?
это наверно самый популярный вопрос связанный со спекой 🙂
хм, что-то не могу найти. Вроде если вызвать instrument то input аргументы будут проверятся
The error fails in the second args predicate that checks (< start end). Note that the :ret and :fn specs are not checked with instrumentation as validating the implementation should occur at testing time.
поэтому тока input параметры дау меня просто запускаются как тесты и все. врубаю руками в при старте репла это instrument иногда
Ох, у меня это больная тема - я опять думаю в PureScript вернуться из-за типчиков но чота хз 🙂
ого, вай?
я помню мы общались с тобой мож пол года назад - ты еще написал что жизнь слишком коротка для хаскеля, я это запомнил 🙂
ну ты напиши потом как освоишся со спекой - норм тебе или всеж типы круче
ой нет
жава?
вроде даже лямбды там есть сча)
а фреймворки типа Spring Boot делают разрабтку на жаве примерно такой же быстрой, как на рельсах, джанге и на всём остальном
дак а почему в итоге к кложуре пришел? или ты чисто для фронта смотришь?
Spring Boot. правда удобен в использовании? оно из преисподни какими-то ексепшанами сыпало...
ну у кложуры есть всё же фишки, плохо реализованные в жаве. REPL тут божественный, макросы и много еще чего. Те же спеки как зависимые типы -- вообще крутяк, их в жаве еще долго не будет....
надо посмотреть его повнимательней
я даж по-первости Spring Boot на кложуре гонял, но че-та не очень красивый код получается
Ну черт знает, вся эта IoC магия, которая разваливается в рантайме. У scala все проще, если скомпилилось, то и работает. Мне еще checked exceptions не хватает, но это совсем субъективно.
нет компайл тайм чека, что все что я хочу заинжектить есть в "скоупе". Если пытаешься жить без IoC, то это не жизнь, а ад. В кложе изначально нет строгих механизмов проверки при компиляции и привыкаешь держать руку на пульсе. У жавы же достаточно строгий компилятор, вон даже на замыкание переменных в лямбду поругивается.
да не поругивается, просто эти переменные изменять нельзя, т.к. там эти лямбды убого сделано
Как лучше оформлять threading вот этот вот? Так:
(->> rules
vals
(apply concat)
(apply max)
inc))))
Или:
(->> (vals rules)
(apply concat)
(apply max)
inc))))
Или:
(->> (vals rules) (apply concat) (apply max) inc)
; (def rules {:birth [3] :keep-alive [2 3]})
(->> rules
(mapcat second)
(reduce max)
inc)
хотя это стилистика больше, меня это apply apply нервируют просто
Во. Мне тоже не хочется apply юзать. Спасибо
(->> rules
(mapcat val)
(reduce max)
inc))))
Вот так дажеval тут понятнее, что делает, чем second, по-моему.
аа оооо
я про него не знал, спасибо 🙂
точнее знал но почему-то юзал second все время. Вот так всегда - помогаешь кому-нить и сам научишся 🙂
трансдьюсеры они типа мапают, фильруют и затем редьюсают. Так? Мб это можно в трансдьюсер организорвать
(->> rules
(mapcat val)
(reduce max)
inc))))
(inc (transduce (mapcat val) max rules))
Что-то не работает
А как пофиксить? Оно в max почему-то 0 аргументов в конце передает
Нужен max, который возвращает nil, когда вызван без аргументов
completing
function
Usage: (completing f)
(completing f cf)
Takes a reducing function f of 2 args and returns a fn suitable for
transduce by adding an arity-1 signature that calls cf (default -
identity) on the result argument.
оно нет?cljs.user=> (time (dotimes [_ 1000000] (->> rules (mapcat val) (reduce max))))
"Elapsed time: 5463.875976 msecs"
cljs.user=> (time (dotimes [_ 1000000] (transduce (mapcat val) (completing max) rules)))
"Elapsed time: 4473.519302 msecs"
Ну вот. И на этот случай есть функция. Отлично. Спасибо
Что-то не так
(transduce (mapcat val) (completing max) rules)
ArityException Wrong number of args (0) passed to: core/max clojure.lang.AFn.throwArity (AFn.java:429)
А. У тебя cljs, у меня clj
ха, в кложуре не робит да)
(transduce (mapcat val) max 0 {:birth [3] :keep-alive [2 3]})
=> 3
Надо было добавить инициализирующее значение
Хорошо, что у меня в этом случае известно, что мнеьше 0. Но в будущем может быть такое, что я не буду знать с чего инициализировать..
дак и юзай обычный mapcat + reduce
(transduce (mapcat val) max Double/NEGATIVE_INFINITY {:birth [3] :keep-alive [2 3]})
Это костылем можно назвать?
конечно
Я думал, что это ловкое решение
ну ок если ты это явно сделаешь 🙂
(def костыль Double/NEGATIVE_INFINITY )
(transduce (mapcat val) max костыль {:birth [3] :keep-alive [2 3]})
о. Правильное называние костылем. Нраица
user=> (time (dotimes [_ 1000000] (transduce (mapcat val) max 0 rules)))
"Elapsed time: 371.018414 msecs"
user=> (time (dotimes [_ 1000000] (->> rules (mapcat val) (reduce max))))
"Elapsed time: 1291.164229 msecs"
в 4 раза быстрее даэто уже на кложе если
Теперь я уверен в том, что примерно понимаю, что должно быть быстрее 🙂 Спасибо
если так проверять, то уж лучше нагенерить структуру на 1м пар, а не 1м раз запускать на 4 элементах
Но в моем контексте эти тесты корректны
ну дада, просто а) лень б) может данные то не будут такими большими в) трансдюсер все равно будет быстрее их для этого и придумали чтоб промежуточные коллекции не строить
кста по кол-ву букв вышло один в один обе версии)
(inc (transduce (mapcat val) max 0 {:birth [3] :keep-alive [2 3]}))
@misha Вот такая структура подается, вот такое действие делается. Есть варианты читаемее или быстрее?
(->> rules (mapcat val) (reduce max) inc)
я тоже быстрее прочитаю. Апи трансдюсеров я каждый раз вспоминаю
Ну лан
на 2 парах k v я бы брал ->> обычный, символов столько же, но парсить глазами в разы быстрее
Но я тут выжимаю производительность в каждой строке, так что отсановлюсь на трансдьюсере А код читать буду только я
(inc (transduce (mapcat val) max 0 rules)
(->> rules (mapcat val) (reduce max) inc)
в ->> у тебя 3 мелких независимых кусочка, а в трансдюсе - один здоровый (и инк тривиальный)
И мне от монолитности приятно 🙂
Тогда я-через-2-недели отвечу себе как мне читается и получу с этого опыт
Ну тут компановка мапа и редьсюса
есть кста еще один вариант, оно вроде для этого как раз но не красиво
(->> rules (reduce-kv #(apply max %1 %3) 0) inc)
Не вижу смысла в ->> тут
тру да, но надо запомнить что reduce-kv вообще есть, а то я не знал
(inc (reduce-kv #(apply max %1 %3) 0 rules))
перлово один фиг, а если лямбду именованую сделать то шумно
(inc (reduce-kv fn[acc k v](apply max acc v) 0 rules))
(time (dotimes [_ 1000000] (->> rules (mapcat val) (reduce max))))
"Elapsed time: 805.543937 msecs"
=> nil
(time (dotimes [_ 1000000] (reduce-kv #(apply max %1 %3) 0 rules)))
"Elapsed time: 457.221997 msecs"
=> nil
(time (dotimes [_ 1000000] (transduce (mapcat val) max 0 rules)))
"Elapsed time: 253.945855 msecs"
=> nil
Вот тут функция страшная
окок, читаемость этого однострочника на этот код не повлияет 😄
И отличие там только в выборе филтрации в зависимости от набора аргументов
В одном случае remove, в другом filter
Как мне укомпактить этот код?
ну вынеси их наверх куда и передавай параметром или в чем вопрос?
А умнее никак?
Делаешь пандорический захват, лифтишь в монаду, потом строишь рекурсивную схему (здесь подойдёт зигохистоморфный препроморфизм) как монадический трансформер из категории эндофункторов, и метациклически вычисляешь результат. Любой второкурсник справится. А если делать на анафорических лямбдах — так задачка вообще на пять минут.
(time
(dotimes [_ 1000000]
(transduce (mapcat val) max 0 x))))
"Elapsed time: 15923.850000 msecs"
(time
(dotimes [_ 1000000]
(reduce
(fn [m kv]
(apply max m (second kv)))
nil
x))))
"Elapsed time: 4310.470000 msecs"
(time
(dotimes [_ 1000000]
(reduce-kv #(apply max %1 %3) 0 x))))
"Elapsed time: 2274.900000 msecs"
(time
(dotimes [_ 1000000]
(->> x (mapcat val) (reduce max)))))
"Elapsed time: 7179.510000 msecs"
Что у тебя в x
?
А чо результаты так отличаются?
Ну я пишу на clj
почему трансдюсер так медленнее работает?
у тебя репл к браузеру подрублен?
а ок, я просто в планке игрался, там JavaScriptCore + self hosted CLJS, поэтому мож разнится
тру, я тоже не ожидал
перл опять победил:)
теперь они отличаются присутствием фильтра
(fn [m kv] (apply max (second kv)))
быстрее на 200 в среднем, чем
(fn [m [k v]] (apply max v)))
(fn [m kv] (apply max m (second kv)))
и оно в итоге вообще
"Elapsed time: 4310.470000 msecs" 
Покажи тесты снова
Все вместе и с трансдьюсом
Угу. Вижу
Не очень понимаю в чем проявляется "код как данные" в кложе. В чем смысл?
=> (first '(fn [m kv] (apply max m (second kv))))
fn
=> (second '(fn [m kv] (apply max m (second kv))))
[m kv]
+ в джаве той же ты не можешь собрать хешмап из литералов, тебе нужно навызывать методов императивно
Что такое литералы?
это типа "как пишется, так и читается".
(= {:a 1} (hash-map :a 1))
true
hash-map - конструктор мапы, а {} - литералполучился список, который не eval'нулся
Как литерал evalнуть?
Вот это list выходит:
'(fn [m kv] (apply max m (second kv)))
Или код как данные, но не данные как код?
то, что код - данные, позволяет хранить сорс код в бд (датомик), отправлять/получать код по проводу с сохранением типов, макросы писать
Понятно теперь, да
Спасибо
Дада, макросы это мега клево. Никогда их не пиши, а если думаешь написать то подумай еще раз 😄
да, большинство макросов уже написано, а доменов, где нехватает макроса - не так и много, либо они супер специфичные
Вот есть у меня трасдьюсер:
По скольку это композиция трансдьюсеров, то на производительность не должно влиять, если я объединю эти два фильтра в один?
Т.е. только две декмопозиции vs одна тут влияют?
если ты гейм оф лайф пишешь - там всё влияет. если ты в браузере он-клик прячешь дивку - там пофигу вообще
Просто я хочу сделать первый фильтр опциональным
тогда тебе виднее, но опять же: GoL - сформулированная и изолированная задача, и её можно максимально оптимизировать пренебрегая читабельностью
тебе не надо будет через год нанимать джуниора из университета, чтобы он мог поддерживать и обновлять код
Меняб кто нанял...
Это да, локальных кложа ваканский практически нет. Всем подавай JEE-пацанов на галеры.
Мой кореш 😄 Мб будет такие же вопросы, как я задавать
Я вот хочу поставить игру на паузу при нажатии на пробел. В quil есть пауза? Я не нашел. Только pause-on-error и unpause там же Если паузы нет, то я просто могу остановить процесс игры после того, как будет нажат пробел, но тогда надо хранить состояние игры, а для этого нужна переменная. Или можно обойтись без переменной?
Самый очевидный и не очень правильный вариант обзавестись хэндлером пробела, в нем менять состояние атома, и просто в основном цикле смотреть на состояние атома и переставать рассчитывать следующий шаг "мира".
А какой варинат правильнее?
@kgofhedgehogs, а можно просто (clojure.core.async/<!! ...) добавить для паузы? Если я правильно понял контекст вопроса.
Ну какой контекст: после нажатия пробела сменить один процесс на другой и обратно после еще одного пробела
А как закрыть в intellij файл не сохраняя его?
Нет права на ошибку
Чорт, оно уже сохранило
вроде никак, можно с помощью Local History отматать фарш обратно.
оно сохраняет при потери фокуса.
Пойду гит примотаю сюда
А то я сейчас больно себе сделал
Хорошо, что я закоммитил сюда в чат 🙂
Где выключить эту дьявольскую функцию?
Или хотя бы добавить делей на нее. Типа 5 минут после потери фокуса
Appearance & Behavior > System Settings
Спасибо
Сделал на атоме
В гифке долго ниче не происходит, т.к. я в репле долго настройки применяю