Recommendations
Recommendations for object-oriented programming
No object orientation where Fβ― is good
Type inference works better with function object than object.Member
Simple object hierarchy
β Avoid inheritance
β Prefer type Union and exhaustive pattern matching
Recursive types
This is particularly true for recursive types. You can define a fold function for them.
π The "Recursive types and folds" series, F# for fun and profit
Structural equality
β Avoid class (reference equality by default)
β Prefer a Record or a Union
π Alternatively, consider Struct for performance purposes
β Consider custom structural equality for performance purposes π Custom Equality and Comparison in F#, Compositional IT
Object-oriented recommended use-cases
Encapsulate mutable state β in a class
Group features β in an interface
Expressive, user-friendly API β tuplified methods
Fβ― API consumed in Cβ― β member extensions
Dependency management β injection into the constructor
Tackle higher-order functions limits
Class to encapsulate mutable state
Interface grouping features
serialize and deserialize form a consistent group
β Grouping them in an object makes sense
π‘ Prefer an interface to a Record (not possible with Fable.Remoting)
Parameters are named in the methods
Object easily instantiated with an object expression
User-friendly API
Advantages of OO implementation:
Addmethod overloaded vsadd2,add3functions (2and3= args count)Single
Logmethod withretryPolicyoptional parameter
π Fβ― component design guidelines - Libraries used in Cβ―
Fβ― API consumed in Cβ―
Do not expose this type as is:
π‘ To make it easier to discover the type and use its features in Cβ―
Put everything in a namespace
Augment type with the functionalities implemented in the companion module
π The API consumed in Cβ― is ~equivalent to:
Dependency management
FP based technique
Parametrization of dependencies + partial application
Small-dose approach: few dependencies, few functions involved
Otherwise, quickly tedious to implement and to use
OO technique
Dependency injection
Inject dependencies into the class constructor
Use these dependencies in methods
π Offers a user-friendly API π
β Particularly recommended for encapsulating side-effects : β Connecting to a DB, reading settings...
Trap
Dependencies injected in the constructor make sense only if they are used throughout the class.
A dependency used in a single method indicates a design smell.
Advanced FP techniques
Dependency rejection = sandwich pattern
Reject dependencies in Application layer, out of Domain layer
Powerful and simple π
... when suitable β
Free monad + interpreter patter
Pure domain
More complex than the sandwich pattern but working in any case
User-friendly through a dedicated computation expression
Reader monad
Only if hidden inside a computation expression
...
π Six approaches to dependency injection, F# for Fun and Profit, Dec 2020
Higher-order function limits
βοΈ It's better to pass an object than a lambda as a parameter to a higher-order function when:
1. Lambda arguments are not explicit
β let test (f: float -> float -> string) =...
β
Solution 1: type wrapping the 2 args float
β f: Point -> string with type Point = { X: float; Y: float }
β
Solution 2: interface + method for named parameters
β type IPointFormatter = abstract Execute : x:float -> y:float -> string
2. Lambda is a command 'T -> unit
'T -> unitβ
Prefer to trigger a side-effect via an object
β type ICommand = abstract Execute : 'T -> unit
3. Lambda: "really" generic!?
β Solution: wrap the function in an object
Last updated
Was this helpful?