1. Allowing user initiated early retry exit. Nothing is stopping some other developer from wrapping your function-with-retries in their own retry-able function. And then another developer even further removed from the details to wrap that one again. Now you have n pow 3 retries and you have services timing out after an hour instead of milliseconds or seconds. Remember that your function-with-retries can just look like a slower flakey function to them. Perhaps exposing the retry variables in the api is the answer, perhaps not.
2. Code readability and debug-ability. Wrapping a piece of work in a function pointer and giving it a generic name like “action” increases cognitive load and makes the codebase slower to read. This is especially true if the contents of the action is so far up the boilerplate chain that the developer has already forgotten what it does. This is a part of the codebase that will be read to troubleshoot the failure so it helps if it is self describing.
3. In my experience, retries are often added when the failure mode is not well understood or as a quick hack to get things over the line. However, we sometimes cannot control what we call so they are an also valid mechanism to use under certain circumstances.
var result = ?, err = NeverEvenTriedError
for attempt_no := 1 to retry_count + 1 do
result, err := action()
while err and is_transient_error(err)
between
sleep()
end
where the "between" block is inserted "between" the iterations or more precisely, it is run before the loop's body for every iteration other than the very first one (after the increment part is executed), so if you put e.g. "print(attempt_no)" in there, it would print 2, 3, etc. up to and including retry_count + 1.
IMHO, such a block would be more useful than the "else" block in loops, although of course, you can always emulate it as
var result = ?, err = NeverEvenTriedError
for attempt_no := 1 to retry_count + 1 do
result, err := action()
while err and is_transient_error(err)
if (attempt_no = 1) then continue
sleep()
end
Well, no. But I think it wouldn't be that hard to add to e.g. Golang, the code gen for this feature is pretty small: find the closest wrapping loop, put "$unique_name := true" as the first statement of its body, replace the keyword itself with "if ($unique_name) { $unique_name = false; continue; }" node.
Sure. You can also simply put what you want to be executed after the condition behind the condition. You could also invent a 4-ary for ( for(init; condition; between; increment) ), but you can also do that with a macro.
We want to loop while there is still an error, the error is still transient, we still haven't retried enough and after we have slept. Pretty straightforward translation of natural language to code.
Here are some more points to consider.
1. Allowing user initiated early retry exit. Nothing is stopping some other developer from wrapping your function-with-retries in their own retry-able function. And then another developer even further removed from the details to wrap that one again. Now you have n pow 3 retries and you have services timing out after an hour instead of milliseconds or seconds. Remember that your function-with-retries can just look like a slower flakey function to them. Perhaps exposing the retry variables in the api is the answer, perhaps not.
2. Code readability and debug-ability. Wrapping a piece of work in a function pointer and giving it a generic name like “action” increases cognitive load and makes the codebase slower to read. This is especially true if the contents of the action is so far up the boilerplate chain that the developer has already forgotten what it does. This is a part of the codebase that will be read to troubleshoot the failure so it helps if it is self describing.
3. In my experience, retries are often added when the failure mode is not well understood or as a quick hack to get things over the line. However, we sometimes cannot control what we call so they are an also valid mechanism to use under certain circumstances.
IMHO, such a block would be more useful than the "else" block in loops, although of course, you can always emulate it as
Is there a language, that has this between keyword?
Well, no. But I think it wouldn't be that hard to add to e.g. Golang, the code gen for this feature is pretty small: find the closest wrapping loop, put "$unique_name := true" as the first statement of its body, replace the keyword itself with "if ($unique_name) { $unique_name = false; continue; }" node.
Sure. You can also simply put what you want to be executed after the condition behind the condition. You could also invent a 4-ary for ( for(init; condition; between; increment) ), but you can also do that with a macro.
What about this?
We want to loop while there is still an error, the error is still transient, we still haven't retried enough and after we have slept. Pretty straightforward translation of natural language to code.