Every Wobble Deserves a Clean Exit
The ambitious part of today’s unicycling session was letting go. The useful part was deciding how to get off before doing so.
Supported starts give a practice attempt a clear beginning: settle onto the saddle, find the pedals, keep a hand on something solid. A controlled release might follow. A controlled dismount should remain available either way. That sounds remarkably like managing a resource in a program: acquire it, use it, release it—even when the interesting bit ends early.
Deterministic resource cleanup makes that last step part of the program’s structure. The idea became central to C++ resource management in the 1990s. Go expresses a related discipline with defer:
package main
import "fmt"
func attempt(release bool) {
fmt.Println("Take support; settle onto saddle")
defer fmt.Println("Controlled dismount; end attempt")
if !release {
return
}
fmt.Println("Release support; keep weight on saddle")
}
func main() {
attempt(false)
attempt(true)
}
The first attempt returns before releasing support. The second reaches the end. Both print the dismount message because the deferred call runs when attempt returns. In working code, that call could close a file or unlock a mutex.
Lua 5.4 gives the cleanup responsibility to a local variable marked <close>:
local exit = {__close = function()
print("Controlled dismount; end attempt")
end}
local function attempt(release)
local guard <close> = setmetatable({}, exit)
print("Take support; settle onto saddle")
if not release then return end
print("Release support; keep weight on saddle")
end
attempt(false)
attempt(true)
Here, leaving the variable’s scope invokes its __close method, including during error unwinding. Go’s deferred calls belong to the surrounding function; Lua’s closing variables belong to their lexical scope. That difference matters inside loops, where postponing every release until function return can keep resources occupied unnecessarily.
Neither example balances a unicycle. These are printed rehearsals of an ownership rule: whoever begins the attempt also arranges its ending. Putting cleanup beside acquisition makes that obligation visible before the code starts wobbling.