Preface |
|
xv | |
Acknowledgments |
|
xvii | |
About This Book |
|
xviii | |
About The Author |
|
xxii | |
Part 1 Getting Started |
|
1 | (58) |
|
1 Introducing functional programming |
|
|
3 | (16) |
|
1.1 What is this thing called functional programming? |
|
|
4 | (5) |
|
Functions as first-class values |
|
|
4 | (1) |
|
|
5 | (1) |
|
Writing programs with strong guarantees |
|
|
6 | (3) |
|
1.2 How functional a language is C#? |
|
|
9 | (9) |
|
The functional nature of LINQ |
|
|
9 | (1) |
|
Shorthand syntax for coding functionally |
|
|
10 | (2) |
|
Language support for tuples |
|
|
12 | (2) |
|
Pattern matching and record types |
|
|
14 | (4) |
|
1.3 What you will learn in this book |
|
|
18 | (1) |
|
|
19 | (14) |
|
2.1 What's a function, anyway? |
|
|
19 | (6) |
|
|
20 | (1) |
|
Representing functions in C# |
|
|
20 | (5) |
|
2.2 Higher-order functions (HOFs) |
|
|
25 | (4) |
|
Functions that depend on other functions |
|
|
25 | (2) |
|
|
27 | (1) |
|
Functions that create other functions |
|
|
28 | (1) |
|
2.3 Using HOFs to avoid duplication |
|
|
29 | (3) |
|
|
32 | (1) |
|
3 Why function purity matters |
|
|
33 | (26) |
|
3.1 What is function purity? |
|
|
34 | (3) |
|
|
34 | (1) |
|
Strategies for managing side effects |
|
|
35 | (1) |
|
|
36 | (1) |
|
3.2 Enabling parallelization by avoiding state mutation |
|
|
37 | (7) |
|
Pure functions parallelize well |
|
|
39 | (1) |
|
Parallelizing impure functions |
|
|
40 | (2) |
|
|
42 | (2) |
|
3.3 Purity and testability |
|
|
44 | (6) |
|
|
44 | (2) |
|
A business validation scenario |
|
|
46 | (2) |
|
Why testing impure functions is hard |
|
|
48 | (2) |
|
3.4 Testing code that performs I/O |
|
|
50 | (7) |
|
Object-oriented dependency injection |
|
|
50 | (4) |
|
Testability without so much boilerplate |
|
|
54 | (3) |
|
3.5 Purity and the evolution of computing |
|
|
57 | (1) |
|
|
57 | (2) |
Part 2 Core Techniques |
|
59 | (78) |
|
4 Designing function signatures and types |
|
|
61 | (14) |
|
4.1 Designing function signatures |
|
|
62 | (2) |
|
Writing functions signatures with arrow notation |
|
|
62 | (1) |
|
How informative is a signature? |
|
|
63 | (1) |
|
4.2 Capturing data with data objects |
|
|
64 | (6) |
|
Primitive types are often not specific enough |
|
|
65 | (1) |
|
Constraining inputs with custom types |
|
|
65 | (2) |
|
|
67 | (2) |
|
Composing values into complex data objects |
|
|
69 | (1) |
|
4.3 Modeling the absence of data with Unit |
|
|
70 | (5) |
|
|
71 | (1) |
|
Bridging the gap between Action and Func |
|
|
72 | (3) |
|
5 Modeling the possible absence of data |
|
|
75 | (20) |
|
5.1 The bad APIs you use every day |
|
|
76 | (1) |
|
5.2 An introduction to the Option type |
|
|
77 | (2) |
|
|
79 | (5) |
|
An idealized implementation of Option |
|
|
79 | (1) |
|
|
80 | (1) |
|
|
81 | (1) |
|
|
82 | (1) |
|
Optimizing the Option implementation |
|
|
83 | (1) |
|
5.4 Option as the natural result type of partial functions |
|
|
84 | (4) |
|
|
85 | (1) |
|
Looking up data in a collection |
|
|
86 | (1) |
|
The smart constructor pattern |
|
|
87 | (1) |
|
|
88 | (6) |
|
Why null is such a terrible idea |
|
|
88 | (1) |
|
Gaining robustness by using Option instead of null |
|
|
89 | (1) |
|
Non-nullable reference types? |
|
|
90 | (2) |
|
Bulletproof against NullReferenceException |
|
|
92 | (2) |
|
|
94 | (1) |
|
6 Patterns in functional programming |
|
|
95 | (22) |
|
6.1 Applying a function to a structure's inner values |
|
|
96 | (5) |
|
Mapping a function onto a sequence |
|
|
96 | (1) |
|
Mapping a function onto an Option |
|
|
97 | (2) |
|
How Option raises the level of abstraction |
|
|
99 | (1) |
|
|
100 | (1) |
|
6.2 Performing side effects with ForEach |
|
|
101 | (2) |
|
6.3 Chaining functions with Bind |
|
|
103 | (5) |
|
Combining Option-returning functions |
|
|
104 | (1) |
|
Flattening nested - lists with Bind |
|
|
105 | (2) |
|
Actually, it's called a monad |
|
|
107 | (1) |
|
|
107 | (1) |
|
Relationship between functors and monads |
|
|
108 | (1) |
|
6.4 Filtering values with Where |
|
|
108 | (1) |
|
6.5 Combining Option and IEnumerable with Bind |
|
|
109 | (2) |
|
6.6 Coding at different levels of abstraction |
|
|
111 | (4) |
|
Regular vs. elevated values |
|
|
111 | (1) |
|
Crossing levels of abstraction |
|
|
112 | (2) |
|
|
114 | (1) |
|
Working at the right level of abstraction |
|
|
114 | (1) |
|
|
115 | (2) |
|
7 Designing programs with function composition |
|
|
117 | (20) |
|
|
118 | (3) |
|
Brushing up on function composition |
|
|
118 | (1) |
|
|
119 | (1) |
|
Composition in the elevated world |
|
|
120 | (1) |
|
7.2 Thinking in terms of data flow |
|
|
121 | (4) |
|
Using LINQ's composable API |
|
|
121 | (2) |
|
Writing functions that compose well |
|
|
123 | (2) |
|
7.3 Programming workflows |
|
|
125 | (3) |
|
A simple workflow for validation |
|
|
125 | (1) |
|
Refactoring with data flow in mind |
|
|
126 | (1) |
|
Composition leads to greater flexibility |
|
|
127 | (1) |
|
7.4 An introduction to functional domain modeling |
|
|
128 | (2) |
|
7.5 An end-to-end server-side workflow |
|
|
130 | (5) |
|
Expressions vs. statements |
|
|
131 | (1) |
|
Declarative vs. imperative |
|
|
132 | (1) |
|
The functional take on layering |
|
|
133 | (2) |
|
|
135 | (2) |
Part 3 Functional Designs |
|
137 | (140) |
|
8 Functional error handling |
|
|
139 | (26) |
|
8.1 A safer way to represent outcomes |
|
|
140 | (6) |
|
Capturing error details with Either |
|
|
140 | (4) |
|
Core functions for working with Either |
|
|
144 | (1) |
|
Comparing Option and Either |
|
|
145 | (1) |
|
8.2 Chaining operations that may fail |
|
|
146 | (2) |
|
8.3 Validation: A perfect use case for Either |
|
|
148 | (3) |
|
Choosing a suitable representation for errors |
|
|
149 | (1) |
|
Defining an Either-based API |
|
|
150 | (1) |
|
|
150 | (1) |
|
8.4 Representing outcomes to client applications |
|
|
151 | (5) |
|
Exposing an Option-like interface |
|
|
153 | (1) |
|
Exposing an Either-like interface |
|
|
154 | (1) |
|
|
155 | (1) |
|
8.5 Variations on the Either theme |
|
|
156 | (7) |
|
Changing between different error representations |
|
|
156 | (1) |
|
Specialized versions of Either |
|
|
157 | (1) |
|
Refactoring to Validation and Exceptional |
|
|
158 | (4) |
|
Leaving exceptions behind? |
|
|
162 | (1) |
|
|
163 | (2) |
|
9 Structuring an application with functions |
|
|
165 | (30) |
|
9.1 Partial application: Supplying arguments piecemeal |
|
|
166 | (5) |
|
Manually enabling partial application |
|
|
168 | (1) |
|
Generalizing partial application |
|
|
169 | (1) |
|
Order of arguments matters |
|
|
170 | (1) |
|
9.2 Overcoming the quirks of method resolution |
|
|
171 | (3) |
|
9.3 Curried functions: Optimized for partial application |
|
|
174 | (2) |
|
9.4 Creating a partial-application-friendly API |
|
|
176 | (4) |
|
|
177 | (1) |
|
Particularizing the data access function |
|
|
178 | (2) |
|
9.5 Modularizing and composing an application |
|
|
180 | (9) |
|
|
181 | (2) |
|
|
183 | (3) |
|
Mapping functions to API endpoints |
|
|
186 | (2) |
|
Comparing the two approaches |
|
|
188 | (1) |
|
9.6 Reducing a list to a single value |
|
|
189 | (4) |
|
|
189 | (2) |
|
Aggregating validation results |
|
|
191 | (1) |
|
Harvesting validation errors |
|
|
192 | (1) |
|
|
193 | (2) |
|
10 Working effectively with multi-argument functions |
|
|
195 | (26) |
|
10.1 Function application in the elevated world |
|
|
196 | (7) |
|
Understanding applicatives |
|
|
198 | (2) |
|
|
200 | (1) |
|
An introduction to property-based testing |
|
|
201 | (2) |
|
10.2 Functors, applicatives, and monads |
|
|
203 | (2) |
|
|
205 | (3) |
|
|
205 | (1) |
|
|
206 | (1) |
|
|
207 | (1) |
|
Using Bind with multi-argument functions |
|
|
208 | (1) |
|
10.4 Improving readability by using LINQ with any monad |
|
|
208 | (7) |
|
Using LINQ with arbitrary functors |
|
|
209 | (1) |
|
Using LINQ with arbitrary monads |
|
|
210 | (4) |
|
The LINQ clauses let, where, and others |
|
|
214 | (1) |
|
10.5 When to use Bind vs. Apply |
|
|
215 | (4) |
|
Validation with smart constructors |
|
|
215 | (1) |
|
Harvesting errors with the applicative flow |
|
|
216 | (2) |
|
Failing fast with the monadic flow |
|
|
218 | (1) |
|
|
219 | (2) |
|
11 Representing state and change |
|
|
221 | (18) |
|
11.1 The pitfalls of state mutation |
|
|
222 | (3) |
|
11.2 Understanding state, identity, and change |
|
|
225 | (4) |
|
|
226 | (2) |
|
Representing change without mutation |
|
|
228 | (1) |
|
11.3 Using records to capture the state of domain entities |
|
|
229 | (6) |
|
Fine-grained control on record initialization |
|
|
231 | (2) |
|
Immutable all the way down |
|
|
233 | (2) |
|
11.4 Separating data and logic |
|
|
235 | (4) |
|
12 A short introduction to functional data structures |
|
|
239 | (13) |
|
12.1 The classic functional linked list |
|
|
240 | (6) |
|
|
243 | (1) |
|
Modifying an immutable list |
|
|
244 | (1) |
|
Destructuring any IEnumerable |
|
|
245 | (1) |
|
|
246 | (4) |
|
|
247 | (2) |
|
|
249 | (1) |
|
|
250 | (1) |
|
|
250 | (2) |
|
13 Event sourcing: A functional approach to persistence |
|
|
252 | (25) |
|
13.1 Thinking functionally about data storage |
|
|
253 | (2) |
|
Why data storage should be append-only |
|
|
253 | (1) |
|
Relax and forget about storing state |
|
|
254 | (1) |
|
13.2 Event sourcing basics |
|
|
255 | (7) |
|
|
256 | (1) |
|
|
257 | (1) |
|
|
258 | (1) |
|
Representing state transitions |
|
|
258 | (2) |
|
Reconstructing the current state from past events |
|
|
260 | (2) |
|
13.3 Architecture of an event-sourced system |
|
|
262 | (10) |
|
|
263 | (3) |
|
|
266 | (1) |
|
|
267 | (2) |
|
Creating views of the data from events |
|
|
269 | (3) |
|
13.4 Comparing different approaches to immutable storage |
|
|
272 | (7) |
|
|
273 | (1) |
|
How event-driven is your domain? |
|
|
273 | (4) |
Part 4 Advanced Techniques |
|
277 | (124) |
|
14 Lazy computations, continuations, and the beauty of monadic composition |
|
|
279 | (23) |
|
14.1 The virtue of laziness |
|
|
280 | (5) |
|
Lazy APIs for working with Option |
|
|
281 | (2) |
|
Composing lazy computations |
|
|
283 | (2) |
|
14.2 Exception handling with Try |
|
|
285 | (6) |
|
Representing computations that may fail |
|
|
286 | (1) |
|
Safely extracting information from a JSON object |
|
|
287 | (1) |
|
Composing computations that may fail |
|
|
288 | (1) |
|
Monadic composition: What does it mean? |
|
|
289 | (2) |
|
14.3 Creating a middleware pipeline for DB access |
|
|
291 | (11) |
|
Composing functions that perform setup/teardown |
|
|
291 | (2) |
|
A recipe against the pyramid of doom |
|
|
293 | (1) |
|
Capturing the essence of a middleware function |
|
|
293 | (2) |
|
Implementing the query pattern for middleware |
|
|
295 | (3) |
|
Adding middleware that times the operation |
|
|
298 | (1) |
|
Adding middleware that manages a DB transaction |
|
|
299 | (3) |
|
15 Stateful programs and stateful computations |
|
|
302 | (16) |
|
15.1 Programs that manage state |
|
|
303 | (6) |
|
|
304 | (2) |
|
Refactoring for testability and error handling |
|
|
306 | (2) |
|
|
308 | (1) |
|
15.2 A language for generating random data |
|
|
309 | (5) |
|
Generating random integers |
|
|
310 | (1) |
|
Generating other primitives |
|
|
310 | (2) |
|
Generating complex structures |
|
|
312 | (2) |
|
15.3 A general pattern for stateful computations |
|
|
314 | (4) |
|
16 Working with asynchronous computations |
|
|
318 | (18) |
|
16.1 Asynchronous computations |
|
|
319 | (11) |
|
|
319 | (1) |
|
Representing asynchronous operations with Task |
|
|
320 | (2) |
|
Task as a container for a future value |
|
|
322 | (2) |
|
|
324 | (2) |
|
An HTTP API for currency conversion |
|
|
326 | (1) |
|
If it fails, try a few more times |
|
|
327 | (1) |
|
Running asynchronous operations in parallel |
|
|
328 | (2) |
|
|
330 | (6) |
|
Reading from a file as an async stream |
|
|
331 | (2) |
|
Consuming async streams functionally |
|
|
333 | (1) |
|
Consuming data from several streams |
|
|
333 | (1) |
|
Aggregation and sorting with async streams |
|
|
334 | (2) |
|
17 Traversable and stacked monads |
|
|
336 | (15) |
|
17.1 Traversables: Working with lists of elevated values |
|
|
336 | (9) |
|
Validating a list of values with monadic Traverse |
|
|
338 | (1) |
|
Harvesting validation errors with applicative Traverse |
|
|
339 | (2) |
|
Applying multiple validators to a single value |
|
|
341 | (1) |
|
Using Traverse with Task to await multiple results |
|
|
342 | (2) |
|
Defining Traverse for single-value structures |
|
|
344 | (1) |
|
17.2 Combining asynchrony and validation (or any other two monadic effects) |
|
|
345 | (6) |
|
The problem of stacked monads |
|
|
345 | (2) |
|
Reducing the number of effects |
|
|
347 | (1) |
|
LINQ expressions with a monad stack |
|
|
348 | (3) |
|
18 Data streams and the Reactive Extensions |
|
|
351 | (25) |
|
18.1 Representing data streams with IObservable |
|
|
352 | (3) |
|
A sequence of values in time |
|
|
353 | (1) |
|
Subscribing to an IObservable |
|
|
354 | (1) |
|
18.2 Creating IObservables |
|
|
355 | (5) |
|
|
356 | (1) |
|
Using Subject to tell an IObservable when it should signal |
|
|
357 | (1) |
|
Creating IObservables from callback-based subscriptions |
|
|
358 | (1) |
|
Creating lObservables from simpler structures |
|
|
359 | (1) |
|
18.3 Transforming and combining data streams |
|
|
360 | (6) |
|
|
360 | (2) |
|
Combining and partitioning streams |
|
|
362 | (2) |
|
Error handling with IObservable |
|
|
364 | (2) |
|
|
366 | (1) |
|
18.4 Implementing logic that spans multiple events |
|
|
366 | (8) |
|
Detecting sequences of pressed keys |
|
|
367 | (2) |
|
Reacting to multiple event sources |
|
|
369 | (2) |
|
Notifying when an account becomes overdrawn |
|
|
371 | (3) |
|
18.5 When should you use IObservable? |
|
|
374 | (2) |
|
19 An introduction to message-passing concurrency |
|
|
376 | (25) |
|
19.1 The need for shared mutable state |
|
|
377 | (1) |
|
19.2 Understanding message-passing concurrency |
|
|
378 | (11) |
|
Implementing agents in C# |
|
|
381 | (1) |
|
Getting started with agents |
|
|
382 | (2) |
|
Using agents to handle concurrent requests |
|
|
384 | (3) |
|
|
387 | (2) |
|
19.3 Functional APIs, agent-based implementations |
|
|
389 | (3) |
|
Agents as implementation details |
|
|
390 | (1) |
|
Hiding agents behind a conventional API |
|
|
391 | (1) |
|
19.4 Message-passing concurrency in LOB applications |
|
|
392 | (9) |
|
Using an agent to synchronize access to account data |
|
|
393 | (1) |
|
Keeping a registry of accounts |
|
|
394 | (1) |
|
An agent is not an object |
|
|
395 | (3) |
|
|
398 | (3) |
Appendix A Working with previous version of C# |
|
401 | (12) |
Epilogue What next? |
|
413 | (2) |
Index |
|
415 | |