I have seen this example in the Arrow documentation:
import arrow.resilience.Schedule
suspend fun main() {
var result = ""
Schedule
.doWhile<String> { input, output -> input.length <= 5 }
.repeat {
result += "a"
result
}
println(result)
}
I don't understand what the purpose of output
is in the doWhile
lambda since it is always the same as input
.
In my personal opinion the Arrow APIs are sometimes a little bit awkward to use. This is just one example, because in this specific case input
and output
will always be the same and are redundant.
A Schedule
has two type parameters, an Input
type and an Output
type. It basically consists of a function that represents a ScheduleStep
that takes an Input
and maps it to a Decision<Input, Output>
. The Output
is specific to the kind of schedule step (the decision), while the Input
must match the type of the repeat
lambda's return value (in your case a String).
You can create a chain of multiple ScheduleStep
s where the Input
will always be the same and just the Output
changes, according to the specific step.
A Schedule that can be used to later call repeat
with a String but delays each repetition by one second could look like this:
val schedule: Schedule<String, Long> = Schedule
.spaced<String>(1.seconds)
The Input
type String must be explicitly set here, but the Output
is a Long, defined by the spaced
function. It just counts how often it was called.
If you want to add a condition to stop the repetition, like you did in your question with doWhile
, you can just append it:
val schedule: Schedule<String, Long> = Schedule
.spaced<String>(1.seconds)
.doWhile { input, output -> input.length <= 5 }
In the doWhile
lambda, the parameter input
is of type String, but the output
parameter is now of type Long, because that is the Output
type of the Schedule returned by spaced
. You can use output
to base your predicate (here input.length <= 5
) on it instead, like this if you want to stop after five iterations:
.doWhile { input, output -> output < 5 }
That is the reason why you have the two different input
and output
parameters in the lambda. Note: Contrary to the example of your question the two parameters are different here.
Btw., the Output
of doWhile
is always the same that was passed in, in this case the same Long that spaced
returned.
Now, back to the example of your question. You use doWhile
directly on the companion object of Schedule, not on an instance of Schedule (as in the examples above):
val schedule: Schedule<String, String> = Schedule
.doWhile<String> { input, output -> input.length <= 5 }
There is no previous Schedule and no previous Output
type, so what happens under the hood is that an identity ScheduleStep is created first that maps the Input
to itself, hence the name. The Output
is not only of the same type as the Input
, the actual values will always be the same too. That is why the parameters input
and output
in the doWhile
lambda above are identical and it doesn't matter which one to use to base your condition on.
Remember, this is only the case when you call doWhile
on the companion object of Schedule (as you did in your question), not if you call it on an instance of Schedule, as in my first examples. I find it confusing to design an API that provides redundant parameters, but maybe the rationale was that the doWhile
function should have the same signature, independent of how it is actually called. I do not know, but that's how it is.