contents


front matter

preface

acknowledgments

about this book

about the author

  

Part 1. Getting started

  1 Introducing functional programming

  1.1  What is this thing called functional programming?

Functions as first-class values

Avoiding state mutation

Writing programs with strong guarantees

  1.2  How functional a language is C#?

The functional nature of LINQ

Shorthand syntax for coding functionally

Language support for tuples

Pattern matching and record types

  1.3  What you will learn in this book

  2 Thinking in functions

  2.1  What’s a function, anyway?

Functions as maps

Representing functions in C#

  2.2  Higher-order functions (HOFs)

Functions that depend on other functions

Adapter functions

Functions that create other functions

  2.3  Using HOFs to avoid duplication

          Exercises

  3 Why function purity matters

  3.1  What is function purity?

Purity and side effects

Strategies for managing side effects

Avoid mutating arguments

  3.2  Enabling parallelization by avoiding state mutation

Pure functions parallelize well

Parallelizing impure functions

Avoiding state mutation

  3.3  Purity and testability

Isolating I/O effects

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

          Exercises

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

Writing honest functions

Composing values into complex data objects

  4.3  Modeling the absence of data with Unit

Why void isn’t ideal

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

  5.3  Implementing Option

An idealized implementation of Option

Consuming an Option

Creating a None

Creating a Some

Optimizing the Option implementation

  5.4  Option as the natural result type of partial functions

Parsing strings

Looking up data in a collection

The smart constructor pattern

  5.5  Dealing with null

Why null is such a terrible idea

Gaining robustness by using Option instead of null

Non-nullable reference types?

Bulletproof against NullReferenceException

          Exercises

  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

Introducing functors

  6.2  Performing side effects with ForEach

  6.3  Chaining functions with Bind

Combining Option-returning functions

Flattening nested lists with Bind

Actually, it’s called a monad

The Return function

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

Regular vs. elevated values

Crossing levels of abstraction

Map vs. Bind, revisited

Working at the right level of abstraction

         Exercises

  7 Designing programs with function composition

  7.1  Function composition

Brushing up on function composition

Method chaining

Composition in the elevated world

  7.2  Thinking in terms of data flow

Using LINQ’s composable API

Writing functions that compose well

  7.3  Programming workflows

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

Expressions vs. statements

Declarative vs. imperative

The functional take on layering

         Exercises

Part 3. Functional designs

  8 Functional error handling

  8.1  A safer way to represent outcomes

Capturing error details with Either

Core functions for working with Either

Comparing Option and Either

  8.2  Chaining operations that may fail

  8.3  Validation: A perfect use case for Either

Choosing a suitable representation for errors

Defining an Either-based API

Adding validation logic

  8.4  Representing outcomes to client applications

Exposing an Option-like interface

Exposing an Either-like interface

Returning a result DTO

  8.5  Variations on the Either theme

Changing between different error representations

Specialized versions of Either

Refactoring to Validation and Exceptional

Leaving exceptions behind?

          Exercises

  9 Structuring an application with functions

  9.1  Partial application: Supplying arguments piecemeal

Manually enabling partial application

Generalizing partial application

Order of arguments matters

  9.2  Overcoming the quirks of method resolution

  9.3  Curried functions: Optimized for partial application

  9.4  Creating a partial-application-friendly API

Types as documentation

Particularizing the data access function

  9.5  Modularizing and composing an application

Modularity in OOP

Modularity in FP

Mapping functions to API endpoints

Comparing the two approaches

  9.6  Reducing a list to a single value

LINQ’s Aggregate method

Aggregating validation results

Harvesting validation errors

          Exercises

10 Working effectively with multi-argument functions

10.1  Function application in the elevated world

Understanding applicatives

Lifting functions

An introduction to property-based testing

10.2  Functors, applicatives, and monads

10.3  The monad laws

Right identity

Left identity

Associativity

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

          Exercises

11 Representing state and change

11.1  The pitfalls of state mutation

11.2  Understanding state, identity, and change

Some things never change

Representing change without mutation

11.3  Using records to capture the state of domain entities

Fine-grained control on record initialization

Immutable all the way down

11.4  Separating data and logic

12 A short introduction to functional data structures

12.1  The classic functional linked list

Common list operations

Modifying an immutable list

Destructuring any IEnumerable

12.2  Binary trees

Common tree operations

Structure sharing

12.3  In conclusion

          Exercises

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

13.2  Event sourcing basics

Representing events

Persisting events

Representing state

Representing state transitions

Reconstructing the current state from past events

13.3  Architecture of an event-sourced system

Handling commands

Handling events

Adding validation

Creating views of the data from events

13.4  Comparing different approaches to immutable storage

Datomic vs. Event Store

How event-driven is your domain?

Part 4. Advanced techniques

14 Lazy computations, continuations, and the beauty of monadic composition

14.1  The virtue of laziness

Lazy APIs for working with Option

Composing lazy computations

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

Caching data in memory

Refactoring for testability and error handling

Stateful computations

15.2  A language for generating random data

Generating random integers

Generating other primitives

Generating complex structures

15.3  A general pattern for stateful computations

16 Working with asynchronous computations

16.1  Asynchronous computations

The need for asynchrony

Representing asynchronous operations with Task

Task as a container for a future value

Handling failure

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)

The problem of stacked monads

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

A sequence of values in time

Subscribing to an IObservable

18.2  Creating IObservables

Creating a timer

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

Stream transformations

Combining and partitioning streams

Error handling with IObservable

Putting it all together

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

Implementing agents in C#

Getting started with agents

Using agents to handle concurrent requests

Agents vs. actors

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

An agent is not an object

Putting it all together

  

Appendix A. Working with previous version of C#

  

Epilogue. What next?

  

index

  

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.80.216