Repeat flow in defflow
Closed this issue ยท 9 comments
I'm facing a use case where I have flows
which need to be run more than once, then check.
Whishes:
(def flow1
(flow do-stuff))
(def flow2
(flow do-stuff))
(defflow
(repeat flow1 and flow2 n times)
(flow "final check"
(match? expected
effects-flow2-flow2-after-repeat)))
How am I doing right now:
(def flow1
(flow do-stuff))
(def flow2
(flow do-stuff))
(defflow
flow1
flow2
flow1
flow2
flow1
flow2
flow1
(flow "final check"
(match? expected
effects-flow2-flow2-after-repeat)))
It will be very handy to be able to set how many times each flow need to be run. if they should be alternate, how many time one should run before the other one get to run.
This test shows my use case:
https://github.com/nubank/mortician/blob/a02293c7f872409af4bbebcaee2c75f40cb2608c/postman/postman/replay_count.clj#L53
Can you help with that please?
Without writing macros you can do something like this to make it a little more concise:
(def flow1+flow2
(flow "two flows in one"
flow1
flow2))
(defflow
flow1+flow2
flow1+flow2
flow1+flow2
(match? ...))
If you want to do something more programmatic, you'll need to write a macro and I think that such a feature is niche enough to not be included in state-flow itself, but rather be a helper inside your own codebase.
The macro would probably look like this
(ns state-flow.scratch
(:require [state-flow.core :as state-flow]
[state-flow.cljtest :refer [match?]]
[state-flow.state :as state]))
(def flow1
(let [run-count (atom 0)]
(state-flow/flow "print the my call count"
(state/wrap-fn #(println (swap! run-count inc))))))
(defmacro flow-repeat [flow n]
`(state-flow/flow ~(str "a flow repeated " n " times")
~@(repeat n flow)))
(clojure.pprint/pprint (macroexpand `(flow-repeat flow1 5)))
(state-flow/run!
(state-flow/flow "counting flow"
(flow-repeat flow1 5)
(match? "whatever"
1
1)))
You could also have the signature take the n
first. I think something like this would work:
(defmacro repeat-flows [n & flows]
`(state-flow/flow ~(str "a flow repeated " n " times")
~@(repeat n flows)))
Then you could call it with inline flows:
(defflow a-flow
(repeat-flows 5 flow1 flow2))
;; or
(defflow a-flow
(repeat-flows 5
(flow "flow1" ,,,)
(flow "flow2" ,,,)))
There is no need for macros:
(m/sequence (repeat n flow1+flow2))
will get what you want. The return value of m/sequence
is the list of all return values, if that is useful.
So:
(defflow my-flow
(m/sequence (repeat n flow1+flow2))
(match? ...))
m/sequence
FYI, you'll find that in the cats.core
namespace: https://github.com/funcool/cats/blob/533c5a7acaf81952307b30840c37e43b759a49f9/src/cats/core.cljc#L781
I implemented a version of sequence
that also takes a predicate, for the purposes of probing: https://github.com/nubank/state-flow/blob/master/src/state_flow/probe.clj#L24
Maybe we could think about exposing m/sequence
and also this one in the spirit of avoiding references to cats.
Deep inside I feel people should just learn how to work with monads though :/
Alternatively we can show this is possible in the docs. Give examples of fmap
and sequence
Deep inside I feel people should just learn how to work with monads
Yeah, I get that state-flow
is your subversive revolution ;)
I think that adding to a README section about writing helpers, with fmap
and sequence
as examples, would be better than exposing more cats
via state-flow
's API, which seems like a bottomless pit to me.
(def sequence m/sequence)
would it be enough? or there is some value in actually wrapping it?
That might be sufficient, but I don't think we should do it. It's not core to state-flow, it's a helper for making helpers. Let's stick to documenting that.
I added a Writing Helpers section to the README.