03.04 — reduce / fold in Ocarina#
One reduce in the whole code base. But it’s the central reducer — it’s what makes chain_actions work.
Code#
# src/ocarina/dsl/testing_with_railway/chain_actions.py
from functools import reduce
def chain_actions[T](
first: ActionSuccess[T], *rest: ActionSuccess[T]
) -> ChainRunner[T]:
def thunk() -> ActionChain[T]:
def reducer(chain: ActionChain[T], step: ActionSuccess[T]) -> ActionChain[T]:
if chain.has_failed():
return chain
return (
chain.then(step.__action__)
.failure(step.__failure_handler__)
.success(step.__success_handler__)
.execute()
)
return reduce(reducer, rest, first.execute())
return ChainRunner(thunk=thunk)
Anatomy#
| FP concept | Python realization |
|---|
| Accumulator type | ActionChain[T] |
| Element type | ActionSuccess[T] |
| Reducer | reducer(chain, step) -> ActionChain[T] |
| Initial value | first.execute() |
| Iterable | rest (the *rest tuple) |
| Short-circuit | if chain.has_failed(): return chain |
| Result | ActionChain[T] (the last one) |
Execution flow#
initial = first.execute() # → ActionChain(ok=True, result=Ok(...))
step 1 : reducer(<initial>, act2) :
chain.has_failed() = False
chain.then(act2.__action__).failure(...).success(...).execute()
→ action raises → Fail
→ failure_handler(exc) ❌ FIRE
→ ActionChain(ok=False, result=Fail(exc))
step 2 : reducer(<failed>, act3) :
chain.has_failed() = True
return chain ⚠️ short-circuit
result of the reduce = <failed ActionChain>
Why reduce#
# Imperative approach (equivalent)
def thunk() -> ActionChain[T]:
chain = first.execute()
for step in rest:
if chain.has_failed():
break
chain = (
chain.then(step.__action__)
.failure(step.__failure_handler__)
.success(step.__success_handler__)
.execute()
)
return chain
| Reduce | Imperative loop |
|---|
A single value flows (the chain) | A state + a mutation |
| Reducer = pure function (testable in isolation) | Inline logic |
| Explicit “accumulator” semantics | Implicit logic |
| Functional standard | Imperative standard |
An expression of intent: we fold a list onto an accumulator. Semantics jump out on the first read. This is what’s called a catamorphism.