Exercise 3: Why Mixed Abstraction Levels Make Purity Harder — Possible Solution ==================================================================== WHAT process_order_mixed ACTUALLY BUNDLES TOGETHER ------------------------------ This chapter's own measurement found process_order_mixed tangling 5 low-level operations (arithmetic, an f-string, a file write) with 1 high-level call, ALL inside one function body. Critically, one of those low-level operations - the file write via open()/f.write() - is not just "low-level," it's a genuine SIDE EFFECT: it changes something outside the function (the contents of orders.log) every time the function runs. Because that side effect is physically embedded in the same function body as the arithmetic and formatting, there is no way to call "just the total calculation part" of process_order_mixed without also triggering the file write - the two concerns can't be separated at the point of use, only inside the function's own single, undifferentiated body. WHY calculate_order_total, log_order, AND send_confirmation_email CAN EACH BE PURE OR CLEANLY SIDE-EFFECTING ON THEIR OWN ------------------------------ Once the concerns are split (this chapter's own process_order_clean), each resulting function has exactly ONE job, which makes its own purity question answerable in isolation: - calculate_order_total(order) touches nothing outside its own arguments and return value - it CAN be, and in this chapter's own example is, genuinely pure. Calling it repeatedly with the same order produces the same total every time, with zero side effects. - log_order(order, total) is explicitly, honestly a side-effecting function (it writes to a file) - but because that's now its ONLY job, the side effect is contained and expected, not smuggled in alongside unrelated arithmetic. Nothing about calling calculate_order_total() risks accidentally triggering it. - send_confirmation_email(customer_email, total) is similarly isolated to exactly one side effect (sending an email), with no arithmetic or logging logic mixed into it. THE UNDERLYING REASON THIS GENERALIZES ------------------------------ Mixing abstraction levels in one function doesn't just make the function harder to READ - it structurally forces every concern inside it, pure or impure, to share the same function boundary. A function can only be evaluated as "pure" or "impure" as a WHOLE - if even one line inside it has a side effect, the entire function is impure, no matter how many of its other lines are genuinely side-effect-free. By tangling a pure calculation with an impure file write, process_order_ mixed forces the pure part to inherit the impure part's own side effect, purely because they share one function body. Separating abstraction levels is what makes it POSSIBLE to ask "is this piece pure?" about each piece individually, rather than being forced to answer "no" for the whole tangled thing at once. WHY THIS WORKS AS AN ANSWER ------------------------------ The answer traces the mechanical reason (a side effect physically embedded in the same function body as pure computation forces the whole function to be impure) rather than only asserting mixed functions are "harder to reason about," and it verifies each of the three split- out functions' own actual purity status individually rather than treating "split into smaller functions" as automatically meaning "pure."