Carving Down to the Essential Value
A greenwood carver starts with a log and removes material until what remains is a spoon. There’s no additive step. The spoon was always in there; the work is subtraction guided by grain.
reduce works the same way. You hand it a collection and a combining function, and it removes structure — peeling away the sequence itself until only the accumulated result survives. In Clojure:
(defn bowl-depth [cuts]
(reduce (fn [depth cut]
(if (= (:direction cut) :with-grain)
(+ depth (:mm cut))
(max 0 (- depth 0.5))))
0
cuts))
(println
(bowl-depth [{:direction :with-grain :mm 2}
{:direction :against-grain :mm 1}
{:direction :with-grain :mm 3}]))
Each cut either deepens the bowl or costs you progress (fighting the grain chips out wood you wanted to keep). The sequence of cuts collapses into a single depth measurement.
Ada predates the functional programming terminology but expresses the same idea through explicit accumulation:
procedure Bowl_Depth_Demo is
type Direction_Kind is (With_Grain, Against_Grain);
type Cut is record
Direction : Direction_Kind;
Mm : Float;
end record;
type Cut_Array is array (Positive range <>) of Cut;
function Bowl_Depth (Cuts : Cut_Array) return Float is
Depth : Float := 0.0;
begin
for Cut of Cuts loop
if Cut.Direction = With_Grain then
Depth := Depth + Cut.Mm;
else
Depth := Float'Max (0.0, Depth - 0.5);
end if;
end loop;
return Depth;
end Bowl_Depth;
begin
null;
end Bowl_Depth_Demo;
The loop variable Depth plays the role of the accumulator. Ada makes the mutation explicit where Clojure hides it inside the fold machinery, but the shape is identical: initial value, combining logic, sequence consumed one element at a time.
John McCarthy’s original LISP had reduce in 1960. Ada 83 standardised the explicit-loop form. Both arrived at the same insight from different directions — one asking “how do we eliminate assignment?” and the other asking “how do we make iteration verifiable?”
The carver’s hook knife follows the grain because cutting against it fractures the fibres unpredictably. The code models exactly that asymmetry: with-grain cuts accumulate cleanly, cross-grain cuts subtract. A reduce is just a controlled collapse — information flowing inward until the collection disappears and only the answer remains.
Sample output, if you’re curious:
4.5
Four and a half millimetres of bowl. Not bad for three passes.