Fork me on GitHub
#clojure-russia
<
2017-02-13
>
dbushenko08:02:56

всем привет!

dbushenko08:02:16

подскажите, а как проверять функции с сайд-эффектами при помощи clojure.spec?

artemyarulin08:02:14

т.е. ты хочешь тестировать в изоляции и мокать IO или ты хочешь реальное IO делать в тестах?

dbushenko08:02:03

мокать хотелось бы

dbushenko08:02:35

мне midje нравится

dbushenko08:02:26

впрочем, как быть с IO впринципе, если я описал спеками грязную функцию?

dbushenko08:02:37

её ж начнёт дергать instrument 100 раз

dbushenko08:02:50

и хочешь-нехочешь, а IO произойдет

artemyarulin08:02:55

Я решил примерно так у себя

(defn get-customer-fn [io id]
  (-> io id do-other-business-logic))

(def get-customer (partial get-customer-fn io/http))
Потом соответсвенно можно покрыть внутреннюю get-customer-fn как угодно. Но это не совсем ответ на твой вопрос, это скорее как на него не отвечать 🙂

dbushenko08:02:37

и как тебе это решение? нравится?

artemyarulin08:02:37

у меня новый проект и пока маленький + мобайл т.е. не так много стейта типа всяких db-connection-pull и прочих традиционных на беке, поэтому данный паттерн идиален при таком масштабе

dbushenko08:02:25

а реально вообще как-то сделать mock без удвоения сущностей?

dbushenko08:02:30

ну вот midje как-то ж делает

artemyarulin08:02:57

ну ага, есть with-чото-там, сек

artemyarulin08:02:26

(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

dbushenko08:02:48

ну вот да, и как, мож там чо можно подменить?

artemyarulin08:02:50

но вроде я как-то читал что это плохя практика нынче, хз, я не юзал никогда

dbushenko08:02:59

я прост вообще не юзал spec, пытаюсь понять, как им тестить

dbushenko08:02:12

почему плохая практика?

artemyarulin08:02:58

или там было про минусы with-bindings точно не помню, я не юзал ни одни из них, не могу сказать

artemyarulin08:02:10

может тебе начать с чистых функций сначала?

dbushenko08:02:12

with-bindings -- да, возможно

dbushenko08:02:29

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

dbushenko08:02:37

а там -- сайд-эффекты

artemyarulin08:02:27

понял да, неа, не могу сказать. Спроси еще в #clojure-spec, там народу поболее

artemyarulin08:02:43

как найдешь ответи запости сюда тоже

dbushenko08:02:44

да спросил уже, чо-та молчат все 🙂

artemyarulin08:02:05

ну а как сейчас (без спеки) ты тестируешь?

dbushenko08:02:19

midje делает моки нормально

dbushenko08:02:43

просто без спеков

dbushenko08:02:48

я хз, мож так и надо....

artemyarulin08:02:53

ааа оо, сек, там есть функция в спеках

artemyarulin08:02:50

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.

urix08:02:58

with-bindings как-то не сильно работает, я использовал with-redefs

artemyarulin08:02:25

(stest/instrument `invoke-service {:stub #{`invoke-service}})
Combining check and instrument параграф в https://clojure.org/guides/spec

dbushenko08:02:37

о, спасибо!

artemyarulin08:02:06

да, чота я затупил, забыл что оно есть, как раз для этого

dbushenko08:02:25

да, это то, что нужно, спасибо!

artemyarulin08:02:42

вот тут статейка от Сиерры по поводу with-bindings Friends Don’t Let Friends Use Dynamic Scope https://stuartsierra.com/2013/03/29/perils-of-dynamic-scope про with-redefs я хз, к стыду не знаю как ни один из них работает 🙂

dbushenko08:02:48

слу, а у тебя есть свои репозитории на гитхабе со спеками? я хотел посмотреть, как структурировать код

dbushenko09:02:39

ну да, мне dynamic scope тож не очень

dbushenko09:02:57

только если очень-очень нужно чё-там там подменить, да и то не припомню, когда такое было

artemyarulin09:02:06

Да блин, я тоже искал и спрашивал в канале спеки, нету ответа, а светлолицый молчит 🙂 Я думаю в зависимости от задачи 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”))

artemyarulin09:02:45

я выбрал пока №3

dbushenko09:02:31

понял, спасибо!

artemyarulin09:02:53

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

dbushenko09:02:36

а у меня, ты знаешь, такая вот мысль есть: сильно глубоко специфицируют тесты, прям уж очень-очень зависимые типы получаются

artemyarulin09:02:39

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

dbushenko09:02:47

если б просто типизацию проверять - уже норм было бы

artemyarulin09:02:30

ну тогда может просто рядом с кодом? там обычно будет однострочник поэтому не слишком шумно

dbushenko09:02:13

а ты не знаешь, можно ли как-то заставить s/fdef срабатывать и на :pre :post?

dbushenko09:02:32

ну т.е. чтобы во время работы функции она проверяла входные и выходные параметры на основе fdef?

artemyarulin09:02:44

это наверно самый популярный вопрос связанный со спекой 🙂

dbushenko09:02:46

чтобы не писать еще :pre 🏣

dbushenko09:02:59

и какой на него популярный ответ?

artemyarulin09:02:09

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

dbushenko09:02:25

а ты как-то проверяешь аргументы?

artemyarulin09:02:00

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 параметры да

dbushenko09:02:34

а чо, ништяк

artemyarulin09:02:34

у меня просто запускаются как тесты и все. врубаю руками в при старте репла это instrument иногда

dbushenko09:02:05

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

dbushenko09:02:23

а то я блин на несколько лет из Clojure в Haskell сбежал, т.к. типов не хватало

artemyarulin09:02:50

Ох, у меня это больная тема - я опять думаю в PureScript вернуться из-за типчиков но чота хз 🙂

dbushenko09:02:10

а я, не поверишь, считаю PS самым отстоем для фронтенда

artemyarulin09:02:16

ого, вай?

dbushenko09:02:27

эти все его сайд-эффекты на каждый чих... трындец.

dbushenko09:02:42

весь UI он построен на грязных, а не на чистых функциях. PS вообще не про это

dbushenko09:02:53

т.е. язык хороший, но не для фронтэнда

artemyarulin09:02:54

я помню мы общались с тобой мож пол года назад - ты еще написал что жизнь слишком коротка для хаскеля, я это запомнил 🙂

artemyarulin09:02:38

ну ты напиши потом как освоишся со спекой - норм тебе или всеж типы круче

dbushenko09:02:49

ну понятно, что типы круче

dbushenko09:02:01

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

dbushenko09:02:12

ну никак не могу заставить себя на ней писать

dbushenko09:02:32

а Haskell, Idris, PureScript -- у них проблемы с инфраструктурой

dbushenko09:02:57

ну вот прикинь, на ней и пишу

artemyarulin09:02:10

вроде даже лямбды там есть сча)

dbushenko09:02:29

так в ней фишка в том, что недостатки языка исправляет хорошая IDE

dbushenko09:02:59

а фреймворки типа Spring Boot делают разрабтку на жаве примерно такой же быстрой, как на рельсах, джанге и на всём остальном

artemyarulin09:02:32

дак а почему в итоге к кложуре пришел? или ты чисто для фронта смотришь?

andmed09:02:57

Spring Boot. правда удобен в использовании? оно из преисподни какими-то ексепшанами сыпало...

dbushenko09:02:27

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

dbushenko09:02:50

Spring Boot -- это то, из-за чего я всё еще не выкинул жаву. Spring Boot офигенен

dbushenko09:02:00

чик-чик -- и в продакшен -- это про него

artemyarulin09:02:09

надо посмотреть его повнимательней

dbushenko09:02:15

всё заранее нормально настроено, тебе только бизнес-логику писать надо

andmed09:02:16

okok надо будет повнимательнее посмотреть. я думал это обертка над старым спрингом.

dbushenko09:02:19

никаких левых настроек

dbushenko09:02:35

да, это так, но это как в рельсах -- opiniated

dbushenko09:02:04

я даж по-первости Spring Boot на кложуре гонял, но че-та не очень красивый код получается

andmed09:02:19

ok включил в тудушку. спасибо

alexander_mihailov09:02:18

Ну черт знает, вся эта IoC магия, которая разваливается в рантайме. У scala все проще, если скомпилилось, то и работает. Мне еще checked exceptions не хватает, но это совсем субъективно.

dbushenko09:02:36

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

dbushenko09:02:51

там же вроде всё относительно просто

dbushenko09:02:28

у нас же тоже есть и component, и mount, и народ не жалуется

alexander_mihailov09:02:22

нет компайл тайм чека, что все что я хочу заинжектить есть в "скоупе". Если пытаешься жить без IoC, то это не жизнь, а ад. В кложе изначально нет строгих механизмов проверки при компиляции и привыкаешь держать руку на пульсе. У жавы же достаточно строгий компилятор, вон даже на замыкание переменных в лямбду поругивается.

dbushenko09:02:09

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

dbushenko09:02:27

ну вообще я тебя понял, если IoC не нравится, то, конечно, Spring лучше не юзать

kgofhedgehogs12:02:10

Как лучше оформлять 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]})

artemyarulin12:02:18

(->> rules 
     (mapcat second) 
     (reduce max) 
     inc)

artemyarulin12:02:05

хотя это стилистика больше, меня это apply apply нервируют просто

kgofhedgehogs12:02:50

Во. Мне тоже не хочется apply юзать. Спасибо

kgofhedgehogs12:02:07

(->> rules
     (mapcat val)
     (reduce max)
     inc))))
Вот так даже

kgofhedgehogs12:02:43

val тут понятнее, что делает, чем second, по-моему.

artemyarulin12:02:59

я про него не знал, спасибо 🙂

artemyarulin12:02:32

точнее знал но почему-то юзал second все время. Вот так всегда - помогаешь кому-нить и сам научишся 🙂

kgofhedgehogs12:02:49

трансдьюсеры они типа мапают, фильруют и затем редьюсают. Так? Мб это можно в трансдьюсер организорвать

kgofhedgehogs12:02:56

(->> rules
     (mapcat val)
     (reduce max)
     inc))))

kgofhedgehogs12:02:42

(inc (transduce (mapcat val) max rules)) Что-то не работает

kgofhedgehogs12:02:02

А как пофиксить? Оно в max почему-то 0 аргументов в конце передает

kgofhedgehogs13:02:04

Нужен max, который возвращает nil, когда вызван без аргументов

artemyarulin13:02:23

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.
оно нет?

artemyarulin13:02:22

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"

kgofhedgehogs13:02:54

Ну вот. И на этот случай есть функция. Отлично. Спасибо

kgofhedgehogs13:02:56

Что-то не так

kgofhedgehogs13:02:06

(transduce (mapcat val) (completing max) rules)

ArityException Wrong number of args (0) passed to: core/max  clojure.lang.AFn.throwArity (AFn.java:429)

kgofhedgehogs13:02:59

А. У тебя cljs, у меня clj

artemyarulin13:02:07

ха, в кложуре не робит да)

kgofhedgehogs13:02:39

(transduce (mapcat val) max 0 {:birth [3] :keep-alive [2 3]}) => 3

kgofhedgehogs13:02:05

Надо было добавить инициализирующее значение

kgofhedgehogs13:02:53

Хорошо, что у меня в этом случае известно, что мнеьше 0. Но в будущем может быть такое, что я не буду знать с чего инициализировать..

artemyarulin13:02:44

дак и юзай обычный mapcat + reduce

kgofhedgehogs13:02:41

(transduce (mapcat val) max Double/NEGATIVE_INFINITY {:birth [3] :keep-alive [2 3]})

kgofhedgehogs13:02:47

Это костылем можно назвать?

artemyarulin13:02:53

конечно

kgofhedgehogs13:02:12

Я думал, что это ловкое решение

artemyarulin13:02:45

ну ок если ты это явно сделаешь 🙂

(def костыль Double/NEGATIVE_INFINITY )
(transduce (mapcat val) max костыль {:birth [3] :keep-alive [2 3]})

kgofhedgehogs13:02:25

о. Правильное называние костылем. Нраица

artemyarulin13:02:07

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 раза быстрее да

artemyarulin13:02:17

это уже на кложе если

kgofhedgehogs13:02:14

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

misha13:02:02

если так проверять, то уж лучше нагенерить структуру на 1м пар, а не 1м раз запускать на 4 элементах

kgofhedgehogs13:02:45

Но в моем контексте эти тесты корректны

artemyarulin13:02:14

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

misha13:02:24

если у тебя в хешмапе 3 пары - так так трансдюсер нафиг не нужен наверное

artemyarulin13:02:03

кста по кол-ву букв вышло один в один обе версии)

misha13:02:05

с т з читаемости

kgofhedgehogs13:02:16

(inc (transduce (mapcat val) max 0 {:birth [3] :keep-alive [2 3]}))

kgofhedgehogs13:02:51

@misha Вот такая структура подается, вот такое действие делается. Есть варианты читаемее или быстрее?

misha13:02:18

с трансдюсером вон вы дебажили дефолтное значение сколько

artemyarulin13:02:03

(->> rules (mapcat val) (reduce max) inc) я тоже быстрее прочитаю. Апи трансдюсеров я каждый раз вспоминаю

misha13:02:37

на 2 парах k v я бы брал ->> обычный, символов столько же, но парсить глазами в разы быстрее

kgofhedgehogs13:02:47

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

kgofhedgehogs13:02:00

(inc (transduce (mapcat val) max 0 rules) (->> rules (mapcat val) (reduce max) inc)

misha13:02:08

ну ты и ты-через-2-недели - 2 разных человека

misha13:02:51

в ->> у тебя 3 мелких независимых кусочка, а в трансдюсе - один здоровый (и инк тривиальный)

kgofhedgehogs13:02:59

И мне от монолитности приятно 🙂

kgofhedgehogs13:02:15

Тогда я-через-2-недели отвечу себе как мне читается и получу с этого опыт

misha13:02:40

ну хз, было бы внутри хотябы 2 мапа скомпонованых

kgofhedgehogs13:02:57

Ну тут компановка мапа и редьсюса

misha13:02:17

для 2 k v - перебор ящитаю

misha13:02:34

но как упражнение - вполне

artemyarulin13:02:45

есть кста еще один вариант, оно вроде для этого как раз но не красиво

(->> rules (reduce-kv #(apply max %1 %3) 0) inc)

misha13:02:02

слишком перл kappa

kgofhedgehogs13:02:24

Не вижу смысла в ->> тут

artemyarulin13:02:27

тру да, но надо запомнить что reduce-kv вообще есть, а то я не знал

kgofhedgehogs13:02:49

(inc (reduce-kv #(apply max %1 %3) 0 rules))

artemyarulin13:02:37

перлово один фиг, а если лямбду именованую сделать то шумно

(inc (reduce-kv fn[acc k v](apply max acc v) 0 rules))

misha13:02:06

ну редюс-кв - сильно много надо аргументов помнить с этими %3

misha13:02:56

и скобочки забыл

kgofhedgehogs13:02:02

(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

kgofhedgehogs13:02:51

Вот тут функция страшная

artemyarulin13:02:08

окок, читаемость этого однострочника на этот код не повлияет 😄

kgofhedgehogs13:02:29

И отличие там только в выборе филтрации в зависимости от набора аргументов

kgofhedgehogs13:02:38

В одном случае remove, в другом filter

kgofhedgehogs13:02:46

Как мне укомпактить этот код?

artemyarulin13:02:41

ну вынеси их наверх куда и передавай параметром или в чем вопрос?

kgofhedgehogs13:02:51

А умнее никак?

artemyarulin13:02:19

Делаешь пандорический захват, лифтишь в монаду, потом строишь рекурсивную схему (здесь подойдёт зигохистоморфный препроморфизм) как монадический трансформер из категории эндофункторов, и метациклически вычисляешь результат. Любой второкурсник справится. А если делать на анафорических лямбдах — так задачка вообще на пять минут.

misha13:02:09

cljs

(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"

misha13:02:56

(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"

kgofhedgehogs14:02:17

Что у тебя в x?

misha14:02:25

то же самое

misha14:02:30

(let [x {:birth [3] :keep-alive [2 3]}]

kgofhedgehogs14:02:45

А чо результаты так отличаются?

misha14:02:12

кложаскрипт, другой лаптоп

kgofhedgehogs14:02:23

Ну я пишу на clj

artemyarulin14:02:37

почему трансдюсер так медленнее работает?

misha14:02:48

ну запустить-то и на js можно потом )

misha14:02:55

это к нолену 😄

artemyarulin14:02:21

у тебя репл к браузеру подрублен?

misha14:02:31

да, там фигвил и хром (что было открыто, там и запустил)

artemyarulin14:02:28

а ок, я просто в планке игрался, там JavaScriptCore + self hosted CLJS, поэтому мож разнится

misha14:02:32

меня удивило, что редюс-кв быстрее, чем редюс

artemyarulin14:02:00

тру, я тоже не ожидал

artemyarulin14:02:23

перл опять победил:)

kgofhedgehogs14:02:30

теперь они отличаются присутствием фильтра

misha14:02:56

это из-за деструктуризации в аргументах

misha14:02:21

@artemyarulin

(fn [m kv] (apply max (second kv)))
быстрее на 200 в среднем, чем
(fn [m [k v]] (apply max v)))

misha14:02:13

ха, лол, у меня там бага

misha14:02:50

(fn [m kv] (apply max m (second kv)))
и оно в итоге вообще "Elapsed time: 4310.470000 msecs" harold

kgofhedgehogs14:02:48

Покажи тесты снова

kgofhedgehogs14:02:05

Все вместе и с трансдьюсом

misha14:02:47

обновил сверху

kgofhedgehogs14:02:51

Угу. Вижу

misha14:02:30

кароче про reduce-kv надо помнить

kgofhedgehogs14:02:37

Не очень понимаю в чем проявляется "код как данные" в кложе. В чем смысл?

misha14:02:44

=> (first '(fn [m kv] (apply max m (second kv))))
fn
=> (second '(fn [m kv] (apply max m (second kv))))
[m kv]

misha14:02:57

+ в джаве той же ты не можешь собрать хешмап из литералов, тебе нужно навызывать методов императивно

kgofhedgehogs14:02:17

Что такое литералы?

misha14:02:38

буквальное представление (репрезентейшн) структуры данных

misha14:02:13

это типа "как пишется, так и читается".

(= {:a 1} (hash-map :a 1))
true
hash-map - конструктор мапы, а {} - литерал

kgofhedgehogs14:02:25

получился список, который не eval'нулся

misha14:02:11

в джаве литералы - инты, строки, и может еще что-то

kgofhedgehogs14:02:55

Как литерал evalнуть?

kgofhedgehogs14:02:50

Вот это list выходит: '(fn [m kv] (apply max m (second kv)))

kgofhedgehogs14:02:34

Или код как данные, но не данные как код?

misha14:02:55

то, что код - данные, позволяет хранить сорс код в бд (датомик), отправлять/получать код по проводу с сохранением типов, макросы писать

misha14:02:40

самое близкое к тебе в повседневной жизни - макросы

kgofhedgehogs14:02:15

Понятно теперь, да

kgofhedgehogs14:02:17

Спасибо

artemyarulin14:02:09

Дада, макросы это мега клево. Никогда их не пиши, а если думаешь написать то подумай еще раз 😄

misha14:02:10

в кложе есть eval, в кложасприте чуть сложнее

misha14:02:27

да, большинство макросов уже написано, а доменов, где нехватает макроса - не так и много, либо они супер специфичные

kgofhedgehogs15:02:00

Вот есть у меня трасдьюсер:

kgofhedgehogs15:02:17

По скольку это композиция трансдьюсеров, то на производительность не должно влиять, если я объединю эти два фильтра в один?

misha15:02:27

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

misha15:02:53

композиция - всегда оверхед, просто мизерный

kgofhedgehogs15:02:20

Т.е. только две декмопозиции vs одна тут влияют?

misha15:02:07

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

kgofhedgehogs15:02:40

Просто я хочу сделать первый фильтр опциональным

misha15:02:57

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

misha15:02:02

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

kgofhedgehogs15:02:44

Меняб кто нанял...

alexander_mihailov15:02:25

Это да, локальных кложа ваканский практически нет. Всем подавай JEE-пацанов на галеры.

kgofhedgehogs15:02:30

Мой кореш 😄 Мб будет такие же вопросы, как я задавать

eremec15:02:00

Не, я молча, как всегда

kgofhedgehogs16:02:59

Я вот хочу поставить игру на паузу при нажатии на пробел. В quil есть пауза? Я не нашел. Только pause-on-error и unpause там же Если паузы нет, то я просто могу остановить процесс игры после того, как будет нажат пробел, но тогда надо хранить состояние игры, а для этого нужна переменная. Или можно обойтись без переменной?

alexander_mihailov16:02:56

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

kgofhedgehogs16:02:39

А какой варинат правильнее?

urix16:02:11

@kgofhedgehogs, а можно просто (clojure.core.async/<!! ...) добавить для паузы? Если я правильно понял контекст вопроса.

misha16:02:26

"просто" opieop

kgofhedgehogs16:02:43

Ну какой контекст: после нажатия пробела сменить один процесс на другой и обратно после еще одного пробела

kgofhedgehogs17:02:49

А как закрыть в intellij файл не сохраняя его?

kgofhedgehogs17:02:20

Нет права на ошибку

kgofhedgehogs17:02:39

Чорт, оно уже сохранило

alexander_mihailov17:02:52

вроде никак, можно с помощью Local History отматать фарш обратно.

alexander_mihailov17:02:03

оно сохраняет при потери фокуса.

kgofhedgehogs17:02:22

Пойду гит примотаю сюда

kgofhedgehogs17:02:34

А то я сейчас больно себе сделал

kgofhedgehogs17:02:07

Хорошо, что я закоммитил сюда в чат 🙂

kgofhedgehogs17:02:38

Где выключить эту дьявольскую функцию?

kgofhedgehogs17:02:58

Или хотя бы добавить делей на нее. Типа 5 минут после потери фокуса

alexander_mihailov17:02:48

Appearance & Behavior > System Settings

kgofhedgehogs17:02:20

Спасибо

kgofhedgehogs17:02:36

Сделал на атоме

kgofhedgehogs17:02:19

В гифке долго ниче не происходит, т.к. я в репле долго настройки применяю