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

  1. Encapsulate mutable state β†’ in a class

  2. Group features β†’ in an interface

  3. Expressive, user-friendly API β†’ tuplified methods

  4. Fβ™― API consumed in Cβ™― β†’ member extensions

  5. Dependency management β†’ injection into the constructor

  6. 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:

  • Add method overloaded vs add2, add3 functions (2 and 3 = args count)

  • Single Log method with retryPolicy optional 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

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

βœ… 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?