A closer analogy to my code would be something like this: the domain logic is still abstracted and encapsulated into separate units; the relationships between those units are preserved (e.g. if we set a breakpoint in `even_step`, it will be triggered by `odd`); etc. However, the loop here is literally just a trampoline for thunks, which makes it highly non-idiomatic for imperative/looping style:
type STEP[T] = either[T, unit => STEP[T]]
stepper[T](current: STEP[T]): T = {
for (result = current; result.isRight(); result = result.value(unit))
return result
}
even(n: uint): boolean = stepper(n match {
case 0: left(true)
case n: right(() => odd(n-1))
})
odd(n: uint): boolean = stepper(n match {
case 0: left(false)
case n: right(() => even(n-1))
})
Instead, we could defunctionalise; but that requires some separate data structure and "interpreter": type STEP = ODD(n: uint) | EVEN(n: uint) | RETURN(x: boolean)
even_impl(n: uint): STEP = n match {
case 0: RETURN(true)
case n: ODD(n-1)
}
odd_impl(n: uint): STEP = n match {
case 0: RETURN(false)
case n: EVEN(n-1)
}
interpret(current: STEP): boolean = {
while (!current.isReturn) {
current = current match {
case ODD(n): odd_impl(n)
case EVEN(n): even_impl(n)
}
}
return current.x
}
odd(n: uint): boolean = interpret(ODD(n))
even(n: uint): boolean = interpret(EVEN(n))