Chapter 14: Functional tools for nested data
The introduction explains why higher-order tools for object values make immutable updates to deeply nested data less awkward.
After this chapter you will be able to
- Tools for nested data. Explain why the chapter extends functional tools from arrays to objects used as hash maps, and what problem those tools address for nested data.
- Motivation for object updates. Explain why the team wants a higher-order function for values in objects: repeated operations that increment or decrement an item's size or quantity still contain duplication, while the higher-order functions used so far operate on arrays.
- Explicit field name argument. Explain how the marketing team removed the implicit argument in function name code smell by making the field name an explicit argument.
- Callback based object updates. Explain how update() replaces several similar functions by taking the object, the key of the value to change, and a callback that performs the operation.
- Updating a keyed value. Explain how to use update() to apply raise10Percent() to the salary field of an employee record.
- Immutable updates and state changes. Distinguish the modified copy returned by update() from changing application state by swapping in that return value.
- Get modify set refactoring. Apply the refactoring by identifying a value read from an object, modified, and set back on the same key using copy-on-write, then replacing those steps with update(). Describe the object, key, and modifying calculation passed to update().
- Focused object value updates. Explain how update() applies a calculation to one value in an object, including the three inputs it requires and what the calculation returns.
- Object value update steps. Trace how update() changes a field in an object, and apply the pattern to an email or quantity field.
- Nested update sequence. Trace the five steps shown for updating the nested options object: get a value at a key twice, produce the new value, then create a modified copy twice.
- Nested update refactoring. Trace how a get–modify–set sequence inside another get–modify–set sequence can be refactored into nested update() calls. Explain why each nested call works at a deeper level of the object.
- Explicit option update arguments. Explain how the section refactors an option update so the option name and the function that modifies it are passed as arguments rather than fixed in the code.
- Two-level nested updates. Explain that update2() works on values two levels deep in objects, uses two keys, and handles the repeated get, modify, and set operations.
- Paths through nested objects. Explain how a sequence of keys locates the size option within an item: first follow 'options', then 'size'.
- Four nested update alternatives. Describe what incrementSizeByName() does and compare the four ways the section presents for writing it, from using update() with incrementSize() to writing the gets, modification, and sets manually.
- Update3 derivation and nesting. Explain how the section derives update3() from the previous option: make the implicit argument explicit, then nest update2() inside update() so the operation reaches one level deeper than update2().
- Recursive nested update. Trace how nestedUpdate() follows a nonempty path, what it does when no keys remain, and what it returns from the nested objects.
- Conditions for safe recursion. Explain how a base case, a recursive case, and progress toward the base case work together to make recursion stop.
- Recursion for nested updates. Explain how a nested update follows the shape of the data: get values on the way down, modify the final value, and use copy-on-write sets on the way back up. Contrast this with processing an array in order.
- Cognitive load of nested paths. Explain why a long path passed to nestedUpdate() is hard to read and remember, especially when an API controls the data model.
- Abstraction barriers for nested data. Describe how an abstraction barrier addresses the difficulty of reasoning about deeply nested data.
- Abstraction barriers for nested data. Explain how meaningful, named operations reduce the cognitive load of working with deeply nested data. Describe how operations for finding a category’s post by ID and modifying its author can hide where those values are stored.
- Array tools and nested updates. Describe how higher-order functions help process arrays and change a particular value in nested data.
- Reusable behavior around callbacks. Explain how higher-order functions codify copy-on-write and logging behavior around supplied functions.
- Functional tools for nested data. Explain that the refactorings used for functional tools on arrays also apply to nested data, and that recursion lets those operations reach arbitrarily deep nesting.
What it builds on
- Functional tool: map() (chapter 12) · Introduces a functional tool that operates over arrays.
- Refactoring: Express implicit argument (chapter 10) · The express implicit argument refactoring introduced earlier.
- Code smell: Implicit argument in function name (chapter 10) · Recognizing an implicit argument encoded in a function name.
- Refactoring: Replace body with callback (chapter 10) · Replacing a function body with a callback.
- Copy-on-write operations on objects (chapter 6) · Copy-on-write operations on objects.
- The three steps of the copy-on-write discipline (chapter 6) · Introduces the copy-on-write discipline.
- Applications have state that changes over time (chapter 6) · Explains how applications use immutable values while state changes over time.
- Abstraction barriers hide implementations (chapter 9) · Introduces the idea that abstraction barriers hide implementations.
Section by section
Introduction
The introduction explains why higher-order tools for object values make immutable updates to deeply nested data less awkward.
- Tools for nested data: Explain why the chapter extends functional tools from arrays to objects used as hash maps, and what problem those tools address for nested data.
Why it matters: It connects the tools you already use for arrays to the difficulty of working immutably with nested objects.
Key terms: higher-order functions, objects as hash maps, deeply nested maps, immutable
14.1 Higher-order functions for values in objects
The team identifies duplicated updates to values inside item objects as a reason to seek a higher-order function for object data, rather than just arrays.
- Motivation for object updates: Explain why the team wants a higher-order function for values in objects: repeated operations that increment or decrement an item's size or quantity still contain duplication, while the higher-order functions used so far operate on arrays.
Why it matters: This identifies the design problem the chapter's object-update tools are meant to address.
Key terms: higher-order functions, values stored in objects, duplication
14.2 Making the field name explicit
The section shows how making a field name an explicit argument removes duplication, then identifies a similar code smell in functions named for their operations.
- Explicit field name argument: Explain how the marketing team removed the implicit argument in function name code smell by making the field name an explicit argument.
Why it matters: Making the changing field explicit lets similar operations share code. - Operation names as implicit arguments: Describe why the refactored functions still looked repetitive when the team added operations such as incrementing, decrementing, doubling, and halving.
Why it matters: Spotting what still varies reveals why duplication remains after the first refactoring.
Key terms: implicit argument in function name, express implicit argument refactoring, field name, operation
14.3 Deriving update()
This section derives update() by replacing several field-specific object operations with one higher-order, copy-on-write function.
- Callback based object updates: Explain how update() replaces several similar functions by taking the object, the key of the value to change, and a callback that performs the operation.
Why it matters: One update function can express different changes to object values without duplicating a function for each operation. - Copy on write foundation: State why update() follows a copy-on-write discipline.
Why it matters: The section connects the new functional tool to the established way of updating objects without abandoning copy-on-write.
Key terms: update(), higher-order function, callback, express implicit argument, replace body with callback, copy-on-write, objectSet()
14.4 Using update() to modify values
Shows how update() applies a value-level function to an object field without mutating the original object.
- Updating a keyed value: Explain how to use update() to apply raise10Percent() to the salary field of an employee record.
Why it matters: You can reuse an operation on a value when that value is stored inside an object. - Immutable updates and state changes: Distinguish the modified copy returned by update() from changing application state by swapping in that return value.
Why it matters: Keeping the calculation separate from the state change makes it clear which step actually changes the application.
Key terms: update(), raise10Percent(), salary key, hash map, copy-on-write, calculation, action, modified copy
14.5 Refactoring: Replace get, modify, set with update()
Shows how to replace a get–modify–set sequence on one object key with a single update() call.
- Get modify set refactoring: Apply the refactoring by identifying a value read from an object, modified, and set back on the same key using copy-on-write, then replacing those steps with update(). Describe the object, key, and modifying calculation passed to update().
Why it matters: This gives you a direct way to simplify a common pattern when working with nested objects.
Key terms: get, modify, set, update(), copy-on-write, callback
14.6 Functional tool: update()
Introduces update() as a functional tool for changing one value under a key in an object.
- Focused object value updates: Explain how update() applies a calculation to one value in an object, including the three inputs it requires and what the calculation returns.
Why it matters: It gives you a focused way to express a change to one value in an object.
Key terms: update(), object, key, calculation, current value
14.7 Visualizing values in objects
This section visualizes how update() changes a value in an object and points out why a nested-object update does not directly match the same pattern.
- Object value update steps: Trace how update() changes a field in an object, and apply the pattern to an email or quantity field.
Why it matters: These steps let you change a field while keeping the operation’s copy-making behavior clear. - Nested update pattern mismatch: Describe why the shirt-size example does not directly match the single-field update() refactoring shown here.
Why it matters: Spotting the extra reads and writes makes clear why nested data needs further attention.
Key terms: update(), modify(), key, modified copy, get, get, modify, set, set
14.8 Visualizing nested updates
This section breaks a nested object update into five steps: two reads, a new value, and two modified copies.
- Nested update sequence: Trace the five steps shown for updating the nested options object: get a value at a key twice, produce the new value, then create a modified copy twice.
Why it matters: This sequence makes the work involved in updating nested data easier to follow.
Key terms: nested options object, value, key, modified copy
14.9 Applying update() to nested data
The section shows how applying the get–modify–set refactoring twice produces nested update() calls for nested objects.
- Nested update refactoring: Trace how a get–modify–set sequence inside another get–modify–set sequence can be refactored into nested update() calls. Explain why each nested call works at a deeper level of the object.
Why it matters: The same refactoring can be applied repeatedly to work with values deeper inside nested objects.
Key terms: get, modify, set, update(), callback, nested objects
14.10 Deriving updateOption()
The section shows how to generalize a nested update by making the option name and modification function explicit arguments.
- Explicit option update arguments: Explain how the section refactors an option update so the option name and the function that modifies it are passed as arguments rather than fixed in the code.
Why it matters: Passing these choices as arguments lets the same function update different options in different ways. - Nested update depth: Describe the relationship the section identifies between nested data and nested calls to update().
Why it matters: This connects the shape of a nested update to the shape of the data it changes.
Key terms: implicit argument, explicit argument, option, modify argument, update(), nested data, nested updates
14.11 Deriving update2()
The section derives update2() by making an implicit argument explicit, yielding a tool for updating values two levels deep in nested objects.
- Two-level nested updates: Explain that update2() works on values two levels deep in objects, uses two keys, and handles the repeated get, modify, and set operations.
Why it matters: It lets callers update a nested value without writing that sequence each time. - Implicit argument refactoring steps: Describe the steps for expressing an implicit argument and how applying that refactoring again makes the function more general.
Why it matters: Making a hidden choice explicit is the step that turns a specialized function into a more general one.
Key terms: update2(), two keys, get, get, modify, set, set, express implicit argument refactoring, implicit argument, explicit argument
14.12 Visualizing update2() on nested objects
The section uses an item’s size option to show how a path locates a value nested inside objects.
- Paths through nested objects: Explain how a sequence of keys locates the size option within an item: first follow 'options', then 'size'.
Why it matters: Paths give you a concise way to describe which nested value an update should reach.
Key terms: path, key, nested objects
14.13 Writing incrementSizeByName() four ways
The section presents four ways to increment a named cart item's size option through three levels of nested data.
- Four nested update alternatives: Describe what incrementSizeByName() does and compare the four ways the section presents for writing it, from using update() with incrementSize() to writing the gets, modification, and sets manually.
Why it matters: These alternatives show how higher-level nested-update tools relate to the operations they replace.
Key terms: incrementSizeByName(), update(), update2(), incrementSize(), gets, modifies, and sets
14.14 Deriving update3()
The section derives update3() by making an implicit argument explicit and nesting update2() inside update() to reach four levels of data.
- Update3 derivation and nesting: Explain how the section derives update3() from the previous option: make the implicit argument explicit, then nest update2() inside update() so the operation reaches one level deeper than update2().
Why it matters: The same nesting pattern suggests how to extend updates to deeper data.
Key terms: update3(), update2(), update(), express implicit argument
14.15 Deriving nestedUpdate()
This section derives nestedUpdate() by replacing depth-specific update functions with a recursive function that follows a path of keys.
- Keys as an explicit path: Explain why nestedUpdate() takes an array of keys instead of encoding the nesting depth in a function name or passing a separate depth argument.
Why it matters: A key path lets one function handle different nesting depths without keeping a separate depth value in sync. - Recursive nested update: Trace how nestedUpdate() follows a nonempty path, what it does when no keys remain, and what it returns from the nested objects.
Why it matters: The recursive step and zero-key case let the same function update a value at any nesting depth. - Recursion for nested data: Explain why recursion fits this nested-data problem and how the section contrasts it with a for loop.
Why it matters: It explains why calling the same function at each level is useful here rather than merely a different way to write a loop.
Key terms: implicit argument in function name, express implicit argument, path of keys, nestedUpdate(), update(), modify(), base case, recursive call, recursion, function call stack
14.16 The anatomy of safe recursion
This section explains the three conditions that keep recursion from running forever: a base case, a recursive case, and progress toward the base case.
- Conditions for safe recursion: Explain how a base case, a recursive case, and progress toward the base case work together to make recursion stop.
Why it matters: These checks help you spot recursion that could run forever.
Key terms: base case, recursive case, progress toward the base case
14.17 Visualizing nestedUpdate()
Introduces a planned visual walkthrough of a three-level nestedUpdate() call that increments a cart item’s size.
14.18 The superpower of recursion
The section explains why updating nested data calls for recursion rather than the straightforward traversal used for arrays.
- Recursion for nested updates: Explain how a nested update follows the shape of the data: get values on the way down, modify the final value, and use copy-on-write sets on the way back up. Contrast this with processing an array in order.
Why it matters: It explains why the familiar array-loop approach does not directly express a nested copy-on-write update.
Key terms: recursion, call stack, nested data, copy-on-write sets
14.19 Design considerations with deep nesting
The section explains why long nested-update paths are hard to understand and introduces abstraction barriers as a way to hide the intermediate data structures.
- Cognitive load of nested paths: Explain why a long path passed to nestedUpdate() is hard to read and remember, especially when an API controls the data model.
Why it matters: Recognizing the source of the difficulty helps you decide when a nested update needs a clearer interface. - Abstraction barriers for nested data: Describe how an abstraction barrier addresses the difficulty of reasoning about deeply nested data.
Why it matters: A barrier can let callers work with nested data without repeatedly reasoning through its internal structure.
Key terms: nestedUpdate(), path, intermediate object, abstraction barrier, layer of functions, implementation
14.20 Abstraction barriers on deeply nested data
The section shows how named abstraction barriers make deeply nested data easier to work with by hiding structural details.
- Abstraction barriers for nested data: Explain how meaningful, named operations reduce the cognitive load of working with deeply nested data. Describe how operations for finding a category’s post by ID and modifying its author can hide where those values are stored.
Why it matters: Good operation names let you work with nested data without repeatedly reasoning through its storage structure.
Key terms: abstraction barriers, deeply nested data, meaningful names, cognitive load
14.21 A summary of our use of higher-order functions
The section summarizes how higher-order functions support array processing, nested updates, copy-on-write operations, and error logging.
- Higher-order function meaning: Explain that a higher-order function takes a function as an argument, returns a function, or both.
Why it matters: This definition connects the section’s examples under one idea. - Array tools and nested updates: Describe how higher-order functions help process arrays and change a particular value in nested data.
Why it matters: These examples show higher-order functions working with both collections and deeply nested values. - Reusable behavior around callbacks: Explain how higher-order functions codify copy-on-write and logging behavior around supplied functions.
Why it matters: These examples show how a higher-order function can package behavior that would otherwise be repeated.
Key terms: higher-order functions, forEach(), map(), filter(), reduce(), update(), nestedUpdate(), copy-on-write discipline, withArrayCopy(), withObjectCopy()
14.22 Conclusion
The conclusion connects earlier functional-tool refactorings to nested data and identifies recursion as the way to handle arbitrary nesting depth.
- Functional tools for nested data: Explain that the refactorings used for functional tools on arrays also apply to nested data, and that recursion lets those operations reach arbitrarily deep nesting.
Why it matters: It connects the nested-data tools to the functional tools used earlier for arrays.
Key terms: refactorings, nested data, recursion, functional tools
14.23 Summary
14.24 Up next…
Previews the application of first-class values and higher-order functions to challenges in distributed systems.
Concepts explained in this chapter
- recursionA programming technique in which a computation is defined in terms of one or more calls to the same computation.
- higher-order functionA function that accepts functions as arguments, returns functions as results, or otherwise treats functions as values.
- extract function argument refactoringA refactoring technique that converts information encoded implicitly in a function name or surrounding code into an explicit function argument.
- implicit argumentA code smell in which a function's name encodes a value that would more appropriately be supplied explicitly as an argument.
- explicit inputAn input to a function that is supplied directly at the call site through an argument or parameter.
- distributed systemA software system whose components communicate over a network and may experience delayed, reordered, duplicated, or lost messages.
- copy-on-writeA data-management technique that creates a copy before modifying data so the original remains unchanged.
- read operationA read operation obtains the current value of data without modifying that data.
- refactoringThe systematic restructuring of existing code without changing its externally observable behavior.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
