Part 1. Getting started
1 Introducing functional programming
1.1 What is this thing called functional programming?
Functions as first-class values
Writing programs with strong guarantees
1.2 How functional a language is C#?
Shorthand syntax for coding functionally
Pattern matching and record types
1.3 What you will learn in this book
2.1 What’s a function, anyway?
2.2 Higher-order functions (HOFs)
Functions that depend on other functions
Functions that create other functions
2.3 Using HOFs to avoid duplication
Strategies for managing side effects
3.2 Enabling parallelization by avoiding state mutation
Pure functions parallelize well
Parallelizing impure functions
A business validation scenario
Why testing impure functions is hard
3.4 Testing code that performs I/O
Object-oriented dependency injection
Testability without so much boilerplate
3.5 Purity and the evolution of computing
Part 2. Core techniques
4 Designing function signatures and types
4.1 Designing function signatures
Writing functions signatures with arrow notation
How informative is a signature?
4.2 Capturing data with data objects
Primitive types are often not specific enough
Constraining inputs with custom types
Composing values into complex data objects
4.3 Modeling the absence of data with Unit
Bridging the gap between Action and Func
5 Modeling the possible absence of data
5.1 The bad APIs you use every day
5.2 An introduction to the Option type
An idealized implementation of Option
Optimizing the Option implementation
5.4 Option as the natural result type of partial functions
Looking up data in a collection
Why null is such a terrible idea
Gaining robustness by using Option instead of null
Bulletproof against NullReferenceException
6 Patterns in functional programming
6.1 Applying a function to a structure’s inner values
Mapping a function onto a sequence
Mapping a function onto an Option
How Option raises the level of abstraction
6.2 Performing side effects with ForEach
6.3 Chaining functions with Bind
Combining Option-returning functions
Flattening nested lists with Bind
Relationship between functors and monads
6.4 Filtering values with Where
6.5 Combining Option and IEnumerable with Bind
6.6 Coding at different levels of abstraction
Crossing levels of abstraction
Working at the right level of abstraction
7 Designing programs with function composition
Brushing up on function composition
Composition in the elevated world
7.2 Thinking in terms of data flow
Writing functions that compose well
A simple workflow for validation
Refactoring with data flow in mind
Composition leads to greater flexibility
7.4 An introduction to functional domain modeling
7.5 An end-to-end server-side workflow
The functional take on layering
Part 3. Functional designs
8.1 A safer way to represent outcomes
Capturing error details with Either
Core functions for working with Either
8.2 Chaining operations that may fail
8.3 Validation: A perfect use case for Either
Choosing a suitable representation for errors
8.4 Representing outcomes to client applications
Exposing an Option-like interface
Exposing an Either-like interface
8.5 Variations on the Either theme
Changing between different error representations
Specialized versions of Either
Refactoring to Validation and Exceptional
9 Structuring an application with functions
9.1 Partial application: Supplying arguments piecemeal
Manually enabling partial application
Generalizing partial application
9.2 Overcoming the quirks of method resolution
9.3 Curried functions: Optimized for partial application
9.4 Creating a partial-application-friendly API
Particularizing the data access function
9.5 Modularizing and composing an application
Mapping functions to API endpoints
9.6 Reducing a list to a single value
Aggregating validation results
10 Working effectively with multi-argument functions
10.1 Function application in the elevated world
An introduction to property-based testing
10.2 Functors, applicatives, and monads
10.3 The monad laws
Using Bind with multi-argument functions
10.4 Improving readability by using LINQ with any monad
Using LINQ with arbitrary functors
Using LINQ with arbitrary monads
The LINQ clauses let, where, and others
10.5 When to use Bind vs. Apply
Validation with smart constructors
Harvesting errors with the applicative flow
Failing fast with the monadic flow
11 Representing state and change
11.1 The pitfalls of state mutation
11.2 Understanding state, identity, and change
Representing change without mutation
11.3 Using records to capture the state of domain entities
Fine-grained control on record initialization
11.4 Separating data and logic
12 A short introduction to functional data structures
12.1 The classic functional linked list
12.2 Binary trees
12.3 In conclusion
13 Event sourcing: A functional approach to persistence
13.1 Thinking functionally about data storage
Why data storage should be append-only
Relax and forget about storing state
Representing state transitions
Reconstructing the current state from past events
13.3 Architecture of an event-sourced system
Creating views of the data from events
13.4 Comparing different approaches to immutable storage
How event-driven is your domain?
Part 4. Advanced techniques
14 Lazy computations, continuations, and the beauty of monadic composition
Lazy APIs for working with Option
14.2 Exception handling with Try
Representing computations that may fail
Safely extracting information from a JSON object
Composing computations that may fail
Monadic composition: What does it mean?
14.3 Creating a middleware pipeline for DB access
Composing functions that perform setup/teardown
A recipe against the pyramid of doom
Capturing the essence of a middleware function
Implementing the query pattern for middleware
Adding middleware that times the operation
Adding middleware that manages a DB transaction
15 Stateful programs and stateful computations
15.1 Programs that manage state
Refactoring for testability and error handling
15.2 A language for generating random data
15.3 A general pattern for stateful computations
16 Working with asynchronous computations
16.1 Asynchronous computations
Representing asynchronous operations with Task
Task as a container for a future value
An HTTP API for currency conversion
If it fails, try a few more times
Running asynchronous operations in parallel
16.2 Async streams
Reading from a file as an async stream
Consuming async streams functionally
Consuming data from several streams
Aggregation and sorting with async streams
17 Traversable and stacked monads
17.1 Traversables: Working with lists of elevated values
Validating a list of values with monadic Traverse
Harvesting validation errors with applicative Traverse
Applying multiple validators to a single value
Using Traverse with Task to await multiple results
Defining Traverse for single-value structures
17.2 Combining asynchrony and validation (or any other two monadic effects)
Reducing the number of effects
LINQ expressions with a monad stack
18 Data streams and the Reactive Extensions
18.1 Representing data streams with IObservable
Using Subject to tell an IObservable when it should signal
Creating IObservables from callback-based subscriptions
Creating IObservables from simpler structures
18.3 Transforming and combining data streams
Combining and partitioning streams
Error handling with IObservable
18.4 Implementing logic that spans multiple events
Detecting sequences of pressed keys
Reacting to multiple event sources
Notifying when an account becomes overdrawn
18.5 When should you use IObservable?
19 An introduction to message-passing concurrency
19.1 The need for shared mutable state
19.2 Understanding message-passing concurrency
Using agents to handle concurrent requests
19.3 Functional APIs, agent-based implementations
Agents as implementation details
Hiding agents behind a conventional API
19.4 Message-passing concurrency in LOB applications
Using an agent to synchronize access to account data
Keeping a registry of accounts
Appendix A. Working with previous version of C#
Epilogue. What next?
3.139.80.216