Подробно опишу суть вопроса.
Базовый пример:
(t/deftest simple-use-case
(let [ef (fn []
(eff
(let [rnd (! [:random])]
(- (* 2. rnd)
1.))))
effect-!>coeffect (fn [effect]
(match effect
[:random] 0.5))
result (loop [[effect continuation] (e/loop-factory ef)]
(if (nil? continuation)
effect
(recur (continuation (effect-!>coeffect effect)))))]
(t/is (= 0.0 result))))Функция ef возвращает случайные числа (-1,1) и
запрашивает результат эффекта [:random] - (0,1)
Для выполнения этой функции нужен внешний обработчик эффектов.
Обработчик в цикле получает эффект и передает обратно коэффект.
e/loop-factory позволяет сделать цикл или синхронным на loop/recur или
рекурсивно-асинхронным:
(fn main-loop [[effect continuation] callback]
(if (nil? continuation)
(callback effect)
(js/setTimeout #(main-loop (continuation (effect-!>coeffect effect))
callback)
0)))
(main-loop (e/loop-factory ef) (fn callback [result] ...))Вопрос
Функция effect-!>coeffect может принимать контекст (state), например так:
(loop [state 0
[effect continuation] (e/loop-factory ef)]
(if (nil? continuation)
effect
(let [[state coeffect] (effect-!>coeffect state effect)]
(recur state (continuation coeffect)))))или останавливать цикл раньше:
(loop [[effect continuation] (e/loop-factory ef x)]
(if (nil? continuation)
effect
(let [coeffect (effect-!>coeffect effect)]
(if (reduced? coeffect)
(unreduced coeffect)
(recur (continuation coeffect))))))Есть способ разделить цикл и дополнительные поведения? Так, чтобы это было похоже на композицию middlewares.
Больше примеров: https://github.com/darkleaf/effect/blob/master/test/darkleaf/effect/core_test.cljc