Typed Pipeline Review¶
Concept Position¶
flowchart TD
family["Python Programming"] --> program["Python Functional Programming"]
program --> module["Module 01: Purity, Substitution, and Local Reasoning"]
module --> concept["Typed Pipeline Review"]
concept --> capstone["Capstone pressure point"]
flowchart TD
problem["Start with the design or failure question"] --> example["Study the worked example and trade-offs"]
example --> boundary["Name the boundary this page is trying to protect"]
boundary --> proof["Carry that question into code review or the capstone"]
Read the first diagram as a placement map: this page is one concept inside its parent module, not a detached essay, and the capstone is the pressure test for whether the idea holds. Read the second diagram as the working rhythm for the page: name the problem, study the example, identify the boundary, then carry one review question forward.
This lesson closes the typed-pipeline hotspot. Adding type parameters is not the win by itself. The win is a pipeline surface that becomes easier to review, harder to misuse, and still equivalent to the simpler untyped baseline.
Review questions¶
- Which type variables actually protect the pipeline from the wrong intermediate values?
- Which helper signatures stay readable enough for another engineer to maintain?
- Which dynamic boundaries should remain dynamic instead of being forced through
Anyor fake generic precision?
Equivalence route¶
When the typed form and the untyped form claim to do the same work, compare them on the same input and demand the same result. The simplest route is:
- run the typed pipeline
- run the untyped baseline
- confirm they return the same value for the same docs and environment
- stop if the typed version only adds ceremony without preventing a real mistake
Property-based review¶
Useful properties for this lesson include:
- stage functions remain deterministic
- chunking still covers the whole cleaned abstract
- embedding stays stable for the same chunk
- the typed full pipeline and the untyped baseline stay equivalent
Bad refactor example¶
The easiest mistake is to pass the wrong intermediate type through a visually similar pipeline step.
from typing import List, Tuple
from funcpipe_rag import RawDoc, Chunk, RagEnv, chunk_doc, clean_doc, embed_chunk
def bad_full_rag(docs: List[RawDoc], env: RagEnv) -> Tuple[Chunk, ...]:
return tuple(
embed_chunk(doc)
for doc in docs
for chunk in chunk_doc(clean_doc(doc), env)
)
That code looks pipeline-shaped, but the intermediate value is wrong. The type surface is only earning its keep if it helps you detect exactly that class of error.
When typed pipelines are worth it¶
Keep the typed layer when:
- higher-order helpers are reused across the module or capstone
- the wrong intermediate type is a realistic maintenance bug
- the signature is still readable after adding the type parameters
- the team uses static checking as part of normal review
Do not force it when:
- the boundary is inherently dynamic and is better protected by validation
- the type expression is harder to understand than the code itself
- the proof still depends entirely on runtime tests and the type adds no signal
Capstone check¶
Before moving on, compare the typed lesson with the module endpoint:
- inspect
capstone/_history/worktrees/module-01/src/funcpipe_rag/fp.py - inspect
capstone/_history/worktrees/module-01/tests/test_laws.py - decide whether the typed helper surface is preventing a real class of mistakes
Reflection¶
- Which helper in your own codebase needs stronger typing because it crosses too many stages?
- Which helper would be clearer with a plain concrete type instead of a generic?
- Which problem is better solved by validation than by more type machinery?
Continue with: Isolating Side Effects