Temporal Recursion

Sometimes we want to perform some actions several times on repeat. This is generally what we do when we create any computer program, but when we are live coding it can be quite tricky to perform repeated actions and continue coding while these actions are performed. A useful solution to this is temporal recursion; creating a function that calls itself in the future. Let’s look at an real life example problem that I was asked about recently and how temporal recursion provided a solution. I was asked why the following FoxDot code did not work:

for n in range(21):
    if n < 4:
        d1 >> play("x ")
    elif n < 20:
        d1 >> play("x-")
    else:
        d1.stop()

The reason is that the code has no concept of time – either seconds or beats – and so all the code is run immediately, in less that 0.1 seconds. That means that after 0.1 seconds, the code calls d1.stop() and, seemingly, nothing happens. If this was a normal Python program, we might tell the program to “sleep” for a small period of time at every step of the loop like so:

for n in range(21):
    if n < 4:
        d1 >> play("x ")
    elif n < 20:
        d1 >> play("x-")
    else:
        d1.stop()
    time.sleep(1)

This would work; for the first 4 seconds of the program we would here the result of d1 >> play("x ") and then for the next 16 seconds we would hear d1 >> play("x-") then it would stop. However, there is no way to stop or change this code while it is running – which is something we like to do when we live code. An alternative approach is to use temporal recursion and define a function that calls itself in the future like so:

def update(n=0):
    if n < 4:
        d1 >> play("x ")
    elif n < 20:
        d1 >> play("x-")
    else:
        d1.stop()
        return
    Clock.future(1, update, args=(n + 1,))

# Start the process by calling the function
update()

The function uses Clock.future to schedule itself in the future with a new value of n until it has the value of 21, then it will stop. The cool thing about temporal recursion is that if we change the contents of the update function, it will still be executed with the correct value for n. We can also stop the repeated call simply by remove the Clock.future call.