The Pitch Only Goes Down
When I tune a steel tongue drum, I only ever file in one direction. To flatten a tongue I grind a little metal from the underside near its root, which softens the spring and drops the pitch. The thing I can never do is put the metal back. Every pass is irreversible, so I work downward on purpose: start a hair sharp, then file toward the target, knowing the note can only ever fall. The sequence of pitches I measure between passes is monotonic — it never increases.
That word, monotonic, is the same one computer science uses for a sequence that moves in only one direction. A monotonic non-increasing sequence is one where every element is less than or equal to the one before it. It sounds trivial until you notice how often it guards correctness: timestamps that must never go backwards, version numbers, the depths in a monotonic stack, the frontier in a merge. If a value that should only decrease suddenly rises, something is wrong upstream.
In Go, checking the property is a single pass, and the filing rule is a clamp that refuses to raise the pitch:
package main
import "fmt"
// A tuning run is valid only if the pitch never rises between passes.
func isNonIncreasing(seq []float64) bool {
for i := 1; i < len(seq); i++ {
if seq[i] > seq[i-1] {
return false
}
}
return true
}
// Filing can approach a lower target but can never push the pitch up.
func file(current, target float64) float64 {
if target >= current {
return current
}
return target
}
func main() {
passes := []float64{440.0, 438.2, 436.5, 435.0}
fmt.Printf("valid tuning: %v\n", isNonIncreasing(passes))
fmt.Printf("toward 450: %.1f\n", file(435.0, 450.0))
fmt.Printf("toward 432: %.1f\n", file(435.0, 432.0))
}
The isNonIncreasing check is the safety net: hand it the four measurements from a real tuning run and it confirms the pitch only fell. The file function encodes the physical constraint directly — ask it to file up to 450 Hz from 435 and it returns 435 unchanged, because there’s no metal to add; ask it to file down to 432 and it obliges. The type system won’t stop me from wanting to go back up, but the function will.
Lua states the same two rules with almost no ceremony:
local function is_non_increasing(seq)
for i = 2, #seq do
if seq[i] > seq[i - 1] then return false end
end
return true
end
local function file(current, target)
if target >= current then return current end
return target
end
local passes = { 440.0, 438.2, 436.5, 435.0 }
print("valid tuning: " .. tostring(is_non_increasing(passes)))
print(string.format("toward 450: %.1f", file(435.0, 450.0)))
print(string.format("toward 432: %.1f", file(435.0, 432.0)))
Both report the run as valid and both refuse to file upward. The clamp is the whole idea: a function that can only ever move its argument one way, no matter how you call it.
There’s a discipline in irreversible processes that I’ve come to appreciate. With software I can usually undo, branch, and try again, so I’m sloppy. With a steel tongue there is no branch — once the pitch is monotonic, it’s monotonic for good. Modelling the constraint in code, instead of trusting myself to remember it, is how I stop sharpening a note I can never recover.