liveBook · Manning

What you will learn in Grokking Simplicity

Taming complex software with functional thinking

by Eric Normand

Chapter by chapter

Chapter 1: Welcome to Grokking Simplicity

This chapter introduction previews the topics and perspective the chapter will cover.

1.1 What is functional programming? · 1.2 The problems with the definition for practical use · 1.3 Problem 1: FP needs side effects · 1.4 Problem 2: FP is good at side effects · 1.5 Problem 3: FP is practical · 1.6 The definition of FP confuses managers · 1.7 We treat functional programming as a set of skills and concepts · 1.8 Distinguishing actions, calculations, and data · 1.9 Functional programmers distinguish code that matters when you call it · 1.10 Functional programmers distinguish inert data from code that does work · 1.11 Functional programmers see actions, calculations, and data · 1.12 Step 1: The user marks a task as completed. · 1.13 Step 2: The client sends a message to the server. · 1.14 Step 3: The server receives the message. · 1.15 Step 4: The server makes a change to its database. · 1.16 Step 5: The server makes a decision of who to notify.** · 1.17 Step 6: The server sends an email notification.** · 1.18 The three categories of code in FP · 1.19 Actions · 1.20 2. Calculations · 1.21 3. Data · 1.23 FP works great for distributed systems, and most software written today is distributed · 1.25 This book is practical for software engineering · 1.26 This book uses real-world scenarios · 1.27 This book focuses on software design · 1.28 This book conveys the richness of FP · 1.29 This book is language agnostic · 1.30 What is functional thinking? · 1.31 Part 1: Distinguishing actions, calculations, and data · 1.32 Part 2: Using first-class abstractions · 1.33 Ground rules for ideas and skills in this book · 1.34 The skills can’t be based on language features · 1.35 2. The skills must have immediate practical benefit · 1.36 3. The skills must apply regardless of your current code situation · 1.37 Conclusion · 1.38 Summary · 1.39 Up next…

What you learn in chapter 1 →

Chapter 2: Functional thinking in action

This chapter introduction previews a broad tour of functional thinking through real-world examples, stratified design, and timelines.

2.1 Welcome to Toni’s Pizza · 2.2 Part 1: Distinguishing actions, calculations, and data · 2.3 Part 2: Using first-class abstractions · 2.4 Part 1: Distinguishing actions, calculations, and data · 2.5 1. Actions · 2.6 Calculations · 2.7 3. Data · 2.9 A first glimpse of stratified design · 2.11 As applied to a robotic kitchen · 2.12 Timelines visualize distributed systems · 2.13 Multiple timelines can execute in different orderings · 2.15 Toni does a postmortem · 2.16 Cutting the timeline: Making the robots wait for each other · 2.18 Retrospective on coordinating robots · 2.19 Conclusion · 2.20 Summary · 2.21 Up next…

What you learn in chapter 2 →

Chapter 3: Distinguishing actions, calculations, and data

This chapter introduction previews applying the actions, calculations, and data categories to problems and code.

3.1 Actions, calculations, and data · 3.2 Actions, calculations, and data apply to any situation · 3.3 Lessons from our shopping process · 3.4 Applying functional thinking to new code · 3.5 Drawing the coupon email process · 3.6 Implementing the coupon email process · 3.7 Applying functional thinking to existing code · 3.8 Actions spread through code · 3.9 Actions can take many forms · 3.10 Conclusion · 3.11 Summary · 3.12 Up next…

What you learn in chapter 3 →

Chapter 4: Extracting calculations from actions

The chapter introduces a planned refactoring of existing software to extract calculations from actions and improve reuse and testability.

4.1 Welcome to MegaMart.com! · 4.2 Calculating free shipping · 4.3 Calculating tax · 4.4 We need to make it more testable · 4.5 We need to make it more reusable · 4.6 Distinguishing actions, calculations, and data · 4.7 Functions have inputs and outputs · 4.8 Testing and reuse relate to inputs and outputs · 4.9 Extracting a calculation from an action · 4.10 Extracting another calculation from an action · 4.11 Let’s see all of our code in one place · 4.12 Conclusion · 4.13 Summary · 4.14 Up next…

What you learn in chapter 4 →

Chapter 5: Improving the design of actions

The chapter introduces why improving actions means reducing, rather than eliminating, their implicit inputs and outputs.

5.1 Aligning design with business requirements · 5.2 Aligning the function with business requirements · 5.3 Principle: Minimize implicit inputs and outputs · 5.4 Reducing implicit inputs and outputs · 5.5 Giving the code a once-over · 5.6 Categorizing our calculations · 5.7 Principle: Design is about pulling things apart · 5.8 Improving the design by pulling add_item() apart · 5.9 Extracting a copy-on-write pattern · 5.10 Using add_item() · 5.11 Categorizing our calculations · 5.12 Smaller functions and more calculations · 5.13 Conclusion · 5.14 Summary · 5.15 Up next…

What you learn in chapter 5 →

Chapter 6: Staying immutable in a mutable language

Introduces the chapter’s plan to use copy-on-write for arrays, objects, and deeply nested data.

6.1 Can immutability be applied everywhere? · 6.2 Categorizing operations into reads, writes, or both · 6.3 The three steps of the copy-on-write discipline · 6.4 Converting a write to a read with copy-on-write · 6.5 Complete diff from mutating to copy-on-write · 6.6 These copy-on-write operations are generalizable · 6.7 JavaScript arrays at a glance · 6.8 What to do if an operation is a read and a write · 6.9 Splitting a function that does a read and write · 6.10 Returning two values from one function · 6.11 Reads to immutable data structures are calculations · 6.12 Applications have state that changes over time · 6.13 Immutable data structures are fast enough · 6.14 Copy-on-write operations on objects · 6.15 JavaScript objects at a glance · 6.16 Converting nested writes to reads · 6.17 What gets copied? · 6.18 Visualizing shallow copies and structural sharing · 6.19 Conclusion · 6.20 Summary · 6.21 Up next…

What you learn in chapter 6 →

Chapter 7: Staying immutable with untrusted code

The chapter introduces the problem of preserving immutability when copy-on-write code interacts with code that may mutate data.

7.1 Immutability with legacy code · 7.2 Our copy-on-write code has to interact with untrusted code · 7.3 Defensive copying defends the immutable original · 7.4 Implementing defensive copies · 7.5 The rules of defensive copying · 7.6 Wrapping untrusted code · 7.7 Defensive copying you may be familiar with · 7.8 Copy-on-write and defensive copying compared · 7.9 Deep copies are more expensive than shallow copies · 7.10 Implementing deep copy in JavaScript is difficult · 7.11 A dialogue between copy-on-write and defensive copying · 7.12 Conclusion · 7.13 Summary · 7.14 Up next…

What you learn in chapter 7 →

Chapter 8: Stratified design: Part 1

The chapter introduces stratified design as writing functions in terms of functions defined in lower layers.

8.1 What is software design? · 8.2 What is stratified design? · 8.3 Developing our design sense · 8.4 Patterns of stratified design · 8.5 Pattern 1: Straightforward implementations · 8.6 Three different zoom levels · 8.7 Extracting the for loop · 8.8 Pattern 1 Review: Straightforward implementation · 8.9 Conclusion · 8.10 Summary · 8.11 Up next…

What you learn in chapter 8 →

Chapter 9: Stratified design: Part 2

The chapter introduces the next part of stratified design, previewing patterns and their relevance to maintenance, testing, and reuse.

9.1 Patterns of stratified design · 9.2 Pattern 2: Abstraction barrier · 9.3 Abstraction barriers hide implementations · 9.4 Ignoring details is symmetrical · 9.5 Swapping the shopping cart’s data structure · 9.6 Re-implementing the shopping cart as an object · 9.7 The abstraction barrier lets us ignore details · 9.8 When to use (and when not to use!) abstraction barriers · 9.9 Pattern 2 Review: Abstraction barrier · 9.10 Our code is more straightforward · 9.11 Pattern 3: Minimal interface · 9.12 Pattern 3 Review: Minimal interface · 9.13 Pattern 4: Comfortable layers · 9.14 Patterns of stratified design · 9.15 What does the graph show us about our code? · 9.16 Code at the top of the graph is easier to change · 9.17 Testing code at the bottom is more important · 9.18 Code at the bottom is more reusable · 9.19 Summary: What the graph shows us about our code · 9.20 Conclusion · 9.21 Summary · 9.22 Up next…

What you learn in chapter 9 →

Chapter 10: First-class functions: Part 1

This introduction previews first-class functions and the code smell and refactorings the chapter will teach.

10.1 Code smell: Implicit argument in function name · 10.2 Marketing still needs to coordinate with dev · 10.3 Code smell: Implicit argument in function name · 10.4 Refactoring: Express implicit argument · 10.5 Recognize what is and what isn’t first-class · 10.6 Will field names as strings lead to more bugs? · 10.7 Will first-class fields make the API hard to change? · 10.8 We will use a lot of objects and arrays · 10.9 First-class functions can replace any syntax · 10.10 For loop example: Eating and cleaning up · 10.11 Refactoring: Replace body with callback · 10.12 What is this syntax? · 10.13 Why are we wrapping the code in a function? · 10.14 Conclusion · 10.15 Summary · 10.16 Up next…

What you learn in chapter 10 →

Chapter 11: First-class functions: Part 2

Introduces the chapter’s planned practice with higher-order functions, copy-on-write, and logging improvements.

11.1 One code smell and two refactorings · 11.2 Refactoring copy-on-write · 11.3 Refactoring copy-on-write for arrays · 11.4 Returning functions from functions · 11.5 Conclusion · 11.6 Summary · 11.7 Up next…

What you learn in chapter 11 →

Chapter 12: Functional iteration

The chapter introduces map(), filter(), and reduce() as useful alternatives to for loops over arrays.

12.1 One code smell and two refactorings · 12.2 MegaMart is creating a communications team · 12.3 Deriving map() from examples · 12.4 Functional tool: map() · 12.5 Three ways to pass a function · 12.6 Example: Email addresses of all customers · 12.7 Deriving filter() from examples · 12.8 Functional tool: filter() · 12.9 Example: Customers with zero purchases · 12.10 Deriving reduce() from examples · 12.11 Functional tool: reduce() · 12.12 Example: Concatenating strings · 12.13 Things you can do with reduce() · 12.14 Three functional tools compared · 12.15 Conclusion · 12.16 Summary · 12.17 Up next…

What you learn in chapter 12 →

Chapter 13: Chaining functional tools

The introduction explains why complex array computations can be expressed as chains of simple functional-tool steps.

13.1 The customer communications team continues · 13.2 Clarifying chains, method 1: Name the steps · 13.3 Clarifying chains, method 2: Naming the callbacks · 13.4 Clarifying chains: Two methods compared · 13.5 Example: Emails of customers who have made one purchase · 13.6 Refactoring existing for loops to functional tools · 13.7 Tip 1: Make data · 13.8 Tip 2: Operate on whole array at once · 13.9 Tip 3: Take many small steps · 13.10 Tip 3: Take many small steps · 13.11 Comparing functional to imperative code · 13.12 Summary of chaining tips · 13.13 Debugging tips for chaining · 13.14 Many other functional tools · 13.15 reduce() for building values · 13.16 Getting creative with data representation · 13.17 Line up those dots · 13.18 Conclusion · 13.19 Summary · 13.20 Up next…

What you learn in chapter 13 →

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.

14.1 Higher-order functions for values in objects · 14.2 Making the field name explicit · 14.3 Deriving update() · 14.4 Using update() to modify values · 14.5 Refactoring: Replace get, modify, set with update() · 14.6 Functional tool: update() · 14.7 Visualizing values in objects · 14.8 Visualizing nested updates · 14.9 Applying update() to nested data · 14.10 Deriving updateOption() · 14.11 Deriving update2() · 14.12 Visualizing update2() on nested objects · 14.13 Writing incrementSizeByName() four ways · 14.14 Deriving update3() · 14.15 Deriving nestedUpdate() · 14.16 The anatomy of safe recursion · 14.17 Visualizing nestedUpdate() · 14.18 The superpower of recursion · 14.19 Design considerations with deep nesting · 14.20 Abstraction barriers on deeply nested data · 14.21 A summary of our use of higher-order functions · 14.22 Conclusion · 14.23 Summary · 14.24 Up next…

What you learn in chapter 14 →

Chapter 15: Isolating timelines

The introduction presents timeline diagrams as a way to see actions over time and reason about bugs, especially in distributed systems.

15.1 There’s a bug! · 15.2 Now we can try to click twice fast · 15.3 The timeline diagram shows what happens over time · 15.4 The two fundamentals of timeline diagrams · 15.5 Two tricky details about the order of actions · 15.6 Drawing the add-to-cart timeline: Step 1 · 15.7 Asynchronous calls require new timelines · 15.8 Different languages, different threading models · 15.9 Building the timeline step-by-step · 15.10 Drawing the add-to-cart timeline: Step 2 · 15.11 Timeline diagrams capture the two kinds of sequential code · 15.12 Timeline diagrams capture the uncertain ordering of parallel code · 15.13 Principles of working with timelines · 15.14 JavaScript’s single-thread · 15.15 JavaScript’s asynchronous queue · 15.16 AJAX and the event queue · 15.17 A complete asynchronous example · 15.18 Simplifying the timeline · 15.19 Reading our finished timeline · 15.20 Simplifying the add-to-cart timeline diagram: Step 3 · 15.21 Review: Drawing the timeline (steps 1–3) · 15.22 Summary: Drawing timeline diagrams · 15.23 Timeline diagrams side-by-side can reveal problems · 15.24 Two slow clicks get the right result · 15.25 Two fast clicks can get the wrong result · 15.26 Timelines that share resources can cause problems · 15.27 Converting a global variable to a local one · 15.28 Converting a global variable to an argument · 15.29 Making our code more reusable · 15.30 Principle: In an asynchronous context, we use a final callback instead of a return value as our explicit output · 15.31 Conclusion · 15.32 Summary · 15.33 Up next…

What you learn in chapter 15 →

Chapter 16: Sharing resources between timelines

The introduction explains why timelines sometimes need to share resources safely, while previewing the chapter’s approach.

16.1 Principles of working with timelines · 16.2 The shopping cart still has a bug · 16.3 We need to guarantee the order of the DOM updates · 16.4 Building a queue in JavaScript · 16.5 Principle: Use real-world sharing as inspiration · 16.6 Making the queue reusable · 16.7 Analyzing the timeline · 16.8 Principle: Analyze the timeline diagram to know if there will be problems · 16.9 Making the queue skip · 16.10 Conclusion · 16.11 Summary · 16.12 Up next…

What you learn in chapter 16 →

Chapter 17: Coordinating timelines

Introduces the need to coordinate timelines even when they do not share an explicit resource.

17.1 Principles of working with timelines · 17.2 There’s a bug! · 17.3 How the code was changed · 17.4 Identify actions: Step 1 · 17.5 Draw each action: Step 2 · 17.6 Simplify the diagram: Step 3 · 17.7 Possible ordering analysis · 17.8 Why this timeline is faster · 17.9 Waiting for both parallel callbacks · 17.10 A concurrency primitive for cutting timelines · 17.11 Using Cut() in our code · 17.12 Uncertain ordering analysis · 17.13 Parallel execution analysis · 17.14 Multiple-click analysis · 17.15 A primitive to call something just once · 17.16 Implicit versus explicit model of time · 17.17 Summary: Manipulating timelines · 17.18 Conclusion · 17.19 Summary · 17.20 Up next…

What you learn in chapter 17 →

Chapter 18: Reactive and onion architectures

Introduces reactive and onion architectures as two patterns the chapter will discuss, without explaining how either works.

18.1 Two separate architectural patterns · 18.2 Coupling of causes and effects of changes · 18.3 What is reactive architecture? · 18.4 Tradeoffs of the reactive architecture · 18.5 Cells are first-class state · 18.6 We can make ValueCells reactive · 18.7 We can update shipping icons when the cell changes · 18.8 FormulaCells calculate derived values · 18.9 Mutable state in functional programming · 18.10 How reactive architecture reconfigures systems · 18.11 Decouples effects from their causes · 18.12 Decoupling manages a center of cause and effect · 18.13 Treat series of steps as pipelines · 18.14 Flexibility in your timeline · 18.15 Two separate architectural patterns · 18.16 What is the onion architecture? · 18.17 Review: Actions, calculations, and data · 18.18 Review: Stratified design · 18.19 Traditional layered architecture · 18.20 A functional architecture · 18.21 Facilitating change and reuse · 18.22 Examine the terms used to place the rule in a layer · 18.23 Analyze readability and awkwardness · 18.24 Conclusion · 18.25 Summary · 18.26 Up next…

What you learn in chapter 18 →

Chapter 19: The functional journey ahead

This chapter introduction closes the book and previews advice on applying functional programming skills and continuing to learn.

19.1 A plan for the chapter · 19.2 We have learned the skills of professionals · 19.3 Big takeaways · 19.4 The ups and downs of skill acquisition · 19.5 Parallel tracks to mastery · 19.6 Sandbox: Start a side project · 19.7 Sandbox: Practice exercises · 19.8 Production: Eliminate a bug today · 19.9 Production: Incrementally improve the design · 19.10 Popular functional languages · 19.11 Functional languages with the most jobs · 19.12 Functional languages by platform · 19.13 Functional languages by learning opportunity · 19.14 Get mathy · 19.15 Further reading · 19.16 Conclusion · 19.17 Summary · 19.18 Up next…

What you learn in chapter 19 →

Concepts explained in this book

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.