As mentioned in my comment, this is applicable wherever we have a "main loop". For example, a game might have a bunch of separate functions/methods to handle the various parts of the game; and a "main loop" which passes the return values of some as arguments to others:
function main() {
world = initWorld()
while(true) {
if (quitPressed()) break;
delta = calculateVelocities(world)
world = updatePositions(delta, world)
collisions = findCollisions(world)
world = handleEvents(mkEvents(collisions), world)
}
print("Goodbye")
quit()
}
Instead of passing data indirectly, by returning to the "main loop"; we could instead have those functions pass their results directly into whatever comes next. For example: function main() { gameStep(initWorld()) }
function gameStep(world) {
if (quitPressed()) quit(print("Goodbye"))
else worldStep(world)
}
function worldStep(world) {
delta = calculateVelocities(world)
updatePositions(delta, world)
}
function updatePositions(delta, world) {
newWorld = <existing logic>
collisions = findCollisions(newWorld)
handleEvents(mkEvents(collisions), newWorld)
}
function handleEvents(events, world) {
newWorld = <existing logic>
gameStep(newWorld)
}
This is a lot more complicated than the even/odd example, but it's the same pattern. In this case, it's probably clear why we wouldn't want to in-line all of the calculations into one gigantic loop, mixing up physics simulation, collision detection, input handling, combat system, and whatever else this game does.Again, not saying this is better/worse than a "main loop" (e.g. it can be helpful to have a "central location" to prevent spaghetti code); but (a) this style isn't possible without tail-call elimination, and (b) those who don't have tail-call elimination maybe wouldn't consider such an implementation.