- HOFs: Higher-Order Functions, receives a function, returns a function or both.
- Pure Function: No side effets, same result for same argument values
- Lazy Evaluation: Compute functions only when needed
- Pattern Matching: Match object against patterns as flow control
- Value Type: Immutable, what they ARE is their identity
- Referential Transparency: Expression can be replaced with its value
Programming into a language
But C# is still known to be an OO language, thru and thu... however,
There is the way to include functional programming concepts into C# without damaging the design.
Objects remain primary building blocks while object interfaces made more functional
- Methods know too much: 1 method is typically responsible for an entire complex operation
- Hard to manage in large projects: complex dependencies are an obstacle in large code bases
- Polymorphism: increase complexity of objects, it's a liability, makes it harder to develop rich models
=> Solution includes functional concepts: model remains OO, with more functional design of classes
- Classes remain
- Operations chopped into classes
- Combine simple functions
- Build larger features out of them
- Make public methods simple
- Make then isolated
- Do not expose complex functions
- Let the consumer compose with small functions
=> It's not all about lambdas and HOFs ;-)
OO underlying issue: it tackles the real-world complexity but leaves a fundamental interconnectedness of all things which is another layer of complexity.
- It's a
struct
- Good: lower runtime memory footprint
- Bad: your consumer is gonna be stuck with that
- Components in
ValueTuple
are public mutable fields (also get stuck with your consumer...)
-
No observable side effects
- Returned value only depends on arguments
- Referentially transparent
-
When defined on objects:
- Classes must be immutable aka value objects:
- Note: It's not a .NET value type
- Can be used like a primitive value
- Can be used an index key
- Semantics:
- Must override
Equals()
andGetHashCode()
- Advise to overload
==
and!=
operators - Advise to also implement
IEquatable<T>
- Value class is
sealed
- Must override
- Arguments and all components must also be immutable
- Classes must be immutable aka value objects:
-
Supports Memoization
- Balance between reduced number of calls and cache hit
- Good for DP problems (ie. reusing previous results as part breaking a problem into smaller ones).
- Remember DP is a generalization of Memoization
- Think about PostSharp to implement memoization out of the box
- Screaming Example: cache results for Fibonacci to reduce the number of calls required to perform computations to 1
-
Benefits
- Same result for the same object and arguments
- Simplify execution model
- Saves us from bugs
- Functional-style
- Capturing strongly typed variables
- Type Matching Expressions (syntax:
if
,switch
[+when
] & ternary)
- Keep behaviour separate from data
- Enclose extensions in a separate C# namespace
- Chaining them (composition), with a rule of thumb: if code not obviously correct then its design is not good, better than one big method, no place left for bugs
- Execution progresses over one track or the other
- Regular tracks may contain an object
- Switch to alternate track when the object ceases to exist
- Defensive Coding: both tracks must be defined
- Applied to both:
Option<T>
which can be a value (ie.Some<T>
) or lack of thereof (ie.None<T>
)Either<TLeft, TRight>
which can be an error (ie.Left<TLeft, Right>
) or a proper value (ie.Right<TLeft, Right>
)