Functional Programming in C#
Enrico Buonanno
  • MEAP began May 2016
  • Publication in July 2017 (estimated)
  • ISBN 9781617293955
  • 325 pages (estimated)
  • printed in black & white

Functional programming is a way of thinking about programs that emphasizes functions, while avoiding state mutation. It allows us to write elegant, intention-revealing code, that shines in testability and support for concurrency. C# includes a number of functional features and libraries, enabling us to take advantage of these benefits.

Functional Programming in C# teaches you to apply functional thinking to real-world scenarios. You'll start by learning the principles of functional programming, and how they translate in the C# language. The book then dives into important topics like function composition, data flow, and principles for designing function signatures, types and collections. Through lots of real-world examples, you'll acquire the tools to tackle programming tasks with a functional approach. The last part of the book deals with advanced topics, including lazy evaluation, stateful computations, asynchrony, and event streams. By the end of this book, you'll be able to integrate functional techniques, making your C# programs robust and maintainable, and helping you become a more well-rounded developer.

"Amazing book for people who know C# and are looking to learn more as well as people who are interested in being more productive and improving their programing skills. Seriously, I can't wait to get my copy."

~ Gonzalo Barba Lopez

"If you are wanting to dip your toes into functional programming, learn techniques for applying FP in C#, or just want to be a better programmer, you should read this."

~ Devon Burriss

Table of Contents detailed table of contents

1. Introducing functional programming

1.1. What is this thing called functional programming?

1.1.1. Functions as first-class values

1.1.2. Avoiding state mutation

1.1.3. Writing programs with strong guarantees

1.2. How functional a language is C#?

1.2.1. The functional nature of LINQ

1.2.2. Functional features in C# 6 and C# 7

1.2.3. A more functional future for C#?

1.3. Thinking in functions

1.3.1. Functions as maps

1.3.2. Representing functions in C#

1.4. Higher-order functions

1.4.1. Functions that depend on other functions

1.4.2. Adapter functions

1.4.3. Functions that create other functions

1.5. In practice: avoiding duplication with HOFs

1.5.1. Encapsulating setup and teardown into a HOF

1.5.2. Turning the using statement into a HOF

1.5.3. Code review: tradeoffs of HOFs

1.6. Benefits of functional programming

1.7. Exercises

1.8. Summary

2. Why function purity matters

2.1. What is function purity?

2.1.1. Purity and side effects

2.1.2. Strategies for managing side effects

2.2. Purity and concurrency

2.2.1. Pure functions parallelize well

2.2.2. Parallelizing impure functions

2.2.3. Avoiding state mutation

2.3. Purity and Testability

====In practice: a validation scenario ==== Bringing impure functions under test ==== Why testing impure functions is hard ==== Parameterized unit tests ==== Avoiding header interfaces === Purity in the context of changes in computing === Exercises === Summary

3. Designing function signatures and types

3.1. Function signature design

3.1.1. Arrow notation

3.1.2. How informative is a signature?

3.2. Capturing data with data objects

3.2.1. Primitive types are often not specific enough

3.2.2. Constraining inputs with custom types

3.2.3. Writing "honest" functions

3.2.4. Composing values with tuples and objects

3.3. Modelling the absence of data with Unit

3.3.1. Why using void is not ideal

3.3.2. Bridging the gap between Action and Func with Unit

3.4. Modelling the possible absence of data with Option

3.4.1. The bad APIs you use every day

3.4.2. An introduction to the Option type

3.4.3. Implementing Option

3.4.4. Gaining robustness by using Option instead of null

3.4.5. Option as the natural result type of partial functions

3.5. Exercises

3.6. Summary

4. Patterns in Functional Programming

4.1. Applying a function to the inner values of a structure

4.1.1. Mapping a function onto a sequence of values

4.1.2. Mapping a function onto an optional value

4.1.3. Introducing Functors

4.2. Performing side effects with ForEach

4.3. The Bind function

4.3.1. Composing Option-returning functions with Bind

4.4. Flattening nested lists with Bind

4.4.1. Actually, it's called a monad

4.4.2. The Return function

4.4.3. Relation between functors and monads

4.5. Filtering values with Where

4.6. Combining Option and IEnumerable with Bind

4.7. Coding at different levels of abstraction

4.7.1. Regular vs elevated values

4.7.2. Crossing levels of abstraction

4.7.3. Map vs Bind, revisited

4.7.4. Working at the right level of abstraction

4.8. Exercises

4.9. Summary

5. Designing programs with function composition

5.1. Function composition

5.1.1. Brushing up on function composition

5.1.2. Method chaining

5.1.3. Composition in the elevated world

5.2. Thinking in terms of data flow

5.2.1. Using LINQ's composable API

5.2.2. Writing functions that compose well

5.3. Programming workflows

5.3.1. A simple workflow for validation

5.3.2. Refactoring with data flow in mind

5.3.3. Composition leads to greater flexibility

5.4. An introduction to functional domain modelling

5.5. An end-to-end server-side workflow

5.5.1. Expressions vs statements

5.5.2. Declarative vs imperative

5.5.3. The functional take on layering

5.6. Exercises

5.7. Summary

6. Functional error handling

6.1. A safer way to represent outcomes

6.1.1. Capturing error details with Either

6.1.2. Core functions for working with Either

6.1.3. Comparing Option and Either

6.2. Chaining operations that may fail

6.3. Validation: a perfect use case for Either

6.3.1. Choosing a suitable representation for errors

6.3.2. Defining an Either-based API

6.3.3. Adding validation logic

6.4. Representing outcomes to client applications

6.4.1. Exposing an Option-like interface

6.4.2. Exposing an Either-like interface

6.4.3. Returning a result DTO

6.5. Variations on the Either theme

6.5.1. Changing between different representations of errors

6.5.2. Specialized versions of Either

6.5.3. Refactoring to Validation and Exceptional

6.5.4. Leaving exceptions behind?

6.6. Exercises

6.7. Summary

7. Structuring an application with functions

7.1. Partial application: supplying arguments piecemeal

7.1.1. Manually enabling partial application

7.1.2. Generalizing partial application

7.1.3. Order of arguments matters

7.2. Overcoming the quirks of method resolution

7.3. Curried functions: optimized for partial application

7.4. Creating a partial-application-friendly API

7.4.1. Types as documentation

7.4.2. Particularizing the data access function

7.5. Modularizing and composing an application

7.5.1. Modularity in OOP

7.5.2. Modularity in FP

7.5.3. Comparing the two

7.5.4. Composing the application

7.6. Reducing a list to a single value

7.6.1. LINQ's Aggregate method

7.6.2. Aggregating validation results

7.6.3. Harvesting validation errors

7.7. Exercises

7.8. Summary

8. Working effectively with multi-argument functions

8.1. Function application in the elevated world

8.1.1. Understanding applicatives

8.1.2. Lifting functions

8.1.3. An introduction to property testing

8.2. Functors, Applicatives, Monads

8.3. The monad laws

8.3.1. Right identity

8.3.2. Left identity

8.3.3. Associativity

8.3.4. Using Bind with multi-argument functions

8.4. Improving readability by using LINQ with any monad

8.4.1. Using LINQ with arbitrary functors

8.4.2. Using LINQ with arbitrary monads

8.4.3. Let, Where and other LINQ clauses

8.5. In practice: when to use Bind vs Apply

8.5.1. Validation with smart constructors

8.5.2. Harvesting errors with the applicative flow

8.5.3. Failing fast with the monadic flow

8.6. Exercises

8.7. Summary

9. Thinking about data functionally

9.1. The pitfalls of state mutation

9.2. Understanding state, identity, and change

9.2.1. Some things never change

9.2.2. Representing change without mutation

9.2.3. Enforcing immutability

9.3. A short introduction to functional data structures

9.3.1. The classic functional linked list

9.3.2. Binary trees

9.4. Exercises

9.5. Summary

10. Event sourcing: a functional approach to persistence

10.1. Thinking functionally about data storage

10.1.1. Why data storage should be append-only

10.1.2. Relax, and forget about storing state

10.2. Event sourcing basics

10.2.1. Representing events

10.2.2. Persisting events

10.2.3. Representing state

10.2.4. An interlude on pattern matching

10.2.5. Representing state transitions

10.2.6. Reconstructing current state from past events

10.3. Architecture of an event sourced system

10.3.1. Handling commands

10.3.2. Handling events

10.3.3. Adding validation

10.3.4. Creating views of the data from events

10.4. Comparing different approaches to immutable storage

10.4.1. Datomic vs Event Store

10.4.2. How event-driven is your domain?

10.5. Summary

11. Lazy computations, continuations, and the beauty of monadic composition

11.1. The virtue of lazyness

11.1.1. Lazy APIs for working with Option

11.1.2. Composing lazy computations

11.2. Exception handling with Try

11.2.1. Representing computations that may fail

11.2.2. Safely extracting information from a JSON object

11.2.3. Composing computations that may fail

11.2.4. Monadic composition: what does it mean?

11.3. Creating a middleware pipeline for DB access

11.3.1. Composing functions that perform setup/teardown

11.3.2. A recipe against the pyramid of doom

11.3.3. Capturing the essence of a middleware function

11.3.4. Implementing the query pattern for Middleware

11.3.5. Adding middleware that times the operation

11.3.6. Adding middleware that manages a DB transaction

11.4. Summary

12. Stateful programs and stateful computations

12.1. Programs that manage state

12.1.1. Maintaining a cache of retrieved resources

12.1.2. Refactoring for testability and error handling

12.1.3. Stateful computations

12.2. A language for generating random data

12.3. A general pattern for stateful computations

12.4. Summary

13. Working with asynchronous computations

13.1. Asynchronous computations

13.1.1. The need for asynchrony

13.1.2. Representing asynchronous operations with Task

13.1.3. Task as a container for a future value

13.1.4. Handling failure

13.1.5. An HTTP API for currency conversion

13.1.6. If it fails, try a few more times

13.1.7. Running asynchronous operations in parallel

13.2. Traversables: working with lists of elevated values

13.2.1. Validating a list of values with monadic Traverse

13.2.2. Harvesting validation errors with applicative Traverse

13.2.3. Applying multiple validators to a single value

13.2.4. Using Traverse with Task to await multiple results

13.2.5. Defining Traverse for single-value structures

13.3. Combining asynchrony and validation (or any other two monadic effects)

13.3.1. The problem of stacked monads

13.3.2. Reducing the number of effects

13.3.3. LINQ expressions with a monad stack

13.4. Summary

14. Data streams and the Reactive Extensions

14.1. Representing data streams with IObservable

14.1.1. A sequence of values in time

14.1.2. Subscribing to an IObservable

14.2. Creating IObservables

14.2.1. Creating a timer

14.2.2. Using Subject to tell an IObservable when it should signal

14.2.3. Creating IObservables form callback-based subscriptions

14.2.4. Creating IObservables from simpler structures

14.3. Transforming and combining data streams

14.3.1. Stream transformations

14.3.2. Combining and partitioning streams

14.3.3. Error handling with IObservable

14.3.4. Putting it all together

14.4. Implementing logic that spans multiple events

14.4.1. Detecting sequences of pressed keys

14.4.2. Reacting to multiple event sources

14.4.3. Notifying when an account becomes overdraft

14.5. When should you use IObservable?

14.6. Summary

15. An introduction to message-passing concurrency

15.1. The need for shared mutable state

15.2. Understanding message-passing concurrency

15.2.1. Implementing agents in C#

15.2.2. Getting started with agents

15.2.3. Using agents to handle concurrent requests

15.2.4. Agents vs. Actors

15.3. Functional APIs, agent-based implementations

15.3.1. Agents as implementation details

15.3.2. Hiding agents behind a conventional API

15.4. Message-passing concurrency in LOB applications

15.4.1. Using an agent to synchronize access to account data

15.4.2. Keeping a registry of accounts

15.4.3. An agent is not an object

15.4.4. Putting it all together

15.5. Summary

What's inside

  • Introduction to functional programming and design
  • Explore C#'s functional programming capabilities
  • Understand how function purity improves testability and support for concurrency
  • Write more readable, declarative code by writing functions that compose
  • Adopt a new approach to error handling and managing state
  • Embrace event-driven design and immutability
  • Leverage concurrency without the pitfalls of shared mutable state
  • Apply functional programming to solve real-world problems

About the reader

This book is designed to help C# programmers with an OOP background understand functional thinking.

About the author

Enrico Buonanno obtained an MS in Computer Science at Columbia University in 2001 and has been working as a software developer and architect since. Working on mission-critical applications in FinTech and other technology-driven businesses has given him a thorough understanding of the demands made on enterprise applications, and how to respond to them with modern designs and techniques.

Manning Early Access Program (MEAP) Read chapters as they are written, get the finished eBook as soon as it’s ready, and receive the pBook long before it's in bookstores.
MEAP combo $49.99 pBook + eBook
MEAP eBook $39.99 pdf + ePub + kindle

FREE domestic shipping on three or more pBooks