Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.
Transcript of Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.
![Page 1: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/1.jpg)
Functional Design Patterns
@ScottWlaschinfsharpforfunandprofit.com
![Page 2: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/2.jpg)
Functional Design Patterns
@ScottWlaschinfsharpforfunandprofit.com
X
Practices? Approach
es?
Tips?
![Page 3: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/3.jpg)
Functional Design Patterns
@ScottWlaschinfsharpforfunandprofit.com
So I'll reluctantly stick with
this...
![Page 4: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/4.jpg)
Not this kind of
pattern nor this
![Page 5: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/5.jpg)
These kind of
patterns
All about recognizing repetition.
![Page 6: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/6.jpg)
This kind of pattern!
A mixture of guidelines, templates, & construction
advice
![Page 7: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/7.jpg)
Functional patterns
• Apomorphisms• Dynamorphisms • Chronomorphisms • Zygohistomorphic
prepromorphismsX
![Page 8: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/8.jpg)
Functional patterns
• Core Principles of FP design– Functions, types, composition
• Functions as parameters– Abstraction, Dependency injection– Partial application, Continuations, Folds,
• Chaining functions– Error handling, Async– Monads
• Dealing with wrapped data – Lifting, Functors – Validation with Applicatives
• Aggregating data and operations– Monoids
![Page 9: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/9.jpg)
This talk
![Page 10: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/10.jpg)
Functional programming is scary
CatamorphismFunctor
Applicativ
e
MonoidMonad
Currying
![Page 11: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/11.jpg)
Functional programming is scary
Catamorphism
MonoidMonad
unfamiliar
CurryingFunctor
“Chainable”
Applicativ
e
“Collapsable”“Mappabl
e”
“Aggregatabl
e”
![Page 12: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/12.jpg)
Object oriented programming is scary
Polymorphism
Inheritan
ce
Covarianc
e
SOLID
InterfaceGenerics
This!
SRP, OCP, LSP, ISP, DIP,
Oh noes..
...don’t forget
IoC, DI, ABC,
MVC, etc., etc...
![Page 13: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/13.jpg)
• Single Responsibility Principle
• Open/Closed principle• Dependency Inversion
Principle• Interface Segregation
Principle• Factory pattern• Strategy pattern• Decorator pattern• Visitor pattern
• Functions• Functions• Functions, also
• Functions
• Yes, functions• Oh my, functions again!• Functions• Functions
OO pattern/principle
FP pattern/principle
Seriously, FP patterns are different
![Page 14: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/14.jpg)
A short and mostly wrong history of programming paradigms
![Page 15: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/15.jpg)
• What we envisioned: – Smalltalk
• What we got: – C++– Object oriented Cobol– PHP– Java
“Worse is better”
Object-Oriented programming
![Page 16: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/16.jpg)
• What we envisioned: – Haskell, OCaml, F#, etc
• What we got: – ??
Functional programming
Please don’t let this happen to FP!
![Page 17: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/17.jpg)
CORE PRINCIPLES OF FP DESIGN
Important to understand!
![Page 18: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/18.jpg)
Core principles of FP design
Steal from mathematics
Types are not classes
Functions are things
Composition everywhere
Function
![Page 19: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/19.jpg)
Core principle:Steal from mathematics
“Programming is one of the most difficult branches of applied mathematics”
- E. W. Dijkstra
![Page 20: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/20.jpg)
Why mathematics
Dijkstra said:
• Mathematical assertions tend to be unusually precise.
• Mathematical assertions tend to be general.They are applicable to a large (often infinite) class of instances.
• Mathematics embodies a discipline of reasoning allowing assertions to be made with an unusually high confidence level.
![Page 21: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/21.jpg)
“Object-oriented programming is an exceptionally bad idea which could only have originated in California.”
E. W. Dijkstra
“You probably know that
arrogance, in computer
science, is measured in
nanodijkstras.”- Alan Kay
![Page 22: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/22.jpg)
Mathematical functions
Function add1(x)input x maps to
x+1
…-10123 …
Domain (int) Codomain (int)…01234…
let add1 x = x + 1
val add1 : int -> int
![Page 23: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/23.jpg)
Function add1(x)input x maps to
x+1
…-10123 …
Domain (int) Codomain (int)…01234…
Mathematical functions
int add1(int input){ switch (input) { case 0: return 1; case 1: return 2; case 2: return 3; case 3: return 4; etc ad infinitum }}
• Input and output values already exist • A function is not a calculation, just a mapping• Input and output values are unchanged
(immutable)
A function doesn’t “do” anything
![Page 24: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/24.jpg)
Mathematical functions
• A (mathematical) function always gives the same output value for a given input value
• A (mathematical) function has no side effects
Function add1(x)input x maps to
x+1
…-10123 …
Domain (int) Codomain (int)…01234…
![Page 25: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/25.jpg)
Functions don't have to be about arithmetic
Function CustomerName(x)input Customer maps to
PersonalName
…Cust1Cust2Cust3Cust3
…
Customer (domain) Name (codomain)…AliceBobSueJohnPam…
Name CustomerName(Customer input){ switch (input) { case Cust1: return “Alice”; case Cust2: return “Bob”; case Cust3: return “Sue”; etc }}
The universe of
Customers
The universe of
Personal Names
![Page 26: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/26.jpg)
Functions can work on functions
Function List.mapint->int maps to int list-
>int list
…add1times2subtract3add42 …
int->int int list -> int list…eachAdd1eachTimes2eachSubtract3eachAdd42…
The universe of
int->int functions
The universe of
int list -> int list
functions
![Page 27: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/27.jpg)
Guideline: Strive for purity
not always
achievable
or desirable
![Page 28: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/28.jpg)
The practical benefits of pure functions
customer.SetName(newName);
let newCustomer = setCustomerName(aCustomer, newName)
Pure functions are easy to reason about
var name = customer.GetName();
let name,newCustomer = getCustomerName(aCustomer)
Is the customer
being changed?
??? I can tell something is
funny
just by looking at the code
Reasoning about code that might not be pure:
Reasoning about code that is pure:
The customer is
being changed.
“Don’t trust the name – trust the
signature”
![Page 29: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/29.jpg)
The practical benefits of pure functions
let x = doSomething()let y = doSomethingElse(x)return y + 1
Pure functions are easy to refactor
![Page 30: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/30.jpg)
The practical benefits of pure functions
Pure functions are easy to refactorlet x = doSomething()let y = doSomethingElse(x)return y + 1
This can be extracted
into a new function
with cut/paste!
![Page 31: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/31.jpg)
The practical benefits of pure functions
Pure functions are easy to refactorlet helper() = let x = doSomething() let y = doSomethingElse(x) return y
return helper() + 1 No Resharper needed!
![Page 32: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/32.jpg)
More practical benefits of pure functions
• Laziness– only evaluate when I need the output
• Cacheable results– same answer every time– "memoization"
• No order dependencies– I can evaluate them in any order I like
• Parallelizable– "embarrassingly parallel"
![Page 33: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/33.jpg)
How to design a pure function
No Side Effects
Immutable Data
PureAwesomene
ss!
Pun intended
![Page 34: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/34.jpg)
How to design a pure function
• Haskell– very pure
• F#, OCaml, Clojure, Scala – easy to be pure
• C#, Java, JavaScript – have to make an effort
The language can help
a lot!
![Page 35: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/35.jpg)
Core principle:Types are not classes
![Page 36: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/36.jpg)
X These are not types
![Page 37: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/37.jpg)
Types are not classes
Set of valid
inputs
Set of valid
outputs
These are types“Int” is a type
“Customer” is a type“int->int” is a
type
So what is a type?
![Page 38: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/38.jpg)
Types separate data from behavior
Lists Lists
List.map
List.collect
List.filter
List.etc
Data
Behavior
![Page 39: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/39.jpg)
Core principle:Functions are things
Function
![Page 40: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/40.jpg)
Functions as things
The Tunnel of Transformation Functionapple -> banana
A function is a standalone thing,
not attached to a class
![Page 41: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/41.jpg)
Functions as things
let z = 1
int-> int->int
1
let add x y = x + y
Same keyword
(not a coincidence)
"z"
"add"
![Page 42: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/42.jpg)
Functions as inputs and outputs
let useFn f = f() + 1
let add x = (fun y -> x + y)
int->(int->int)
int->int int
int int->int
let transformInt f x = (f x) + 1
int->int
int int
Function as output
Function as input
Function as parameter
(int->int)->int
int->int
![Page 43: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/43.jpg)
Core principle:Composition everywhere
![Page 44: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/44.jpg)
Function composition
Function 1apple -> banana
Function 2banana -> cherry
![Page 45: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/45.jpg)
Function composition
>>
Composition
Function 1apple -> banana
Function 2banana -> cherry
![Page 46: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/46.jpg)
Function composition
New Functionapple -> cherry
New function
Can't tell it was built from smaller functions!
![Page 47: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/47.jpg)
Types can be composed too
“algebraic types"
![Page 48: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/48.jpg)
Product types
× =Alice, Jan 12th Bob, Feb 2nd
Carol, Mar 3rd
Set of people
Set of dates
type Birthday = Person * Date
"product type"
![Page 49: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/49.jpg)
Sum types
Set of Cash values
Set of Cheque values
Set of CreditCard values
+
+
type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber
"sum type"
![Page 50: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/50.jpg)
DDD & DOMAIN MODELLING
![Page 51: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/51.jpg)
Domain modelling pattern: Use types to represent
constraints
Avoid “primitive
obsession”
![Page 52: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/52.jpg)
Types represent constraints on input and output
type Suit = Club | Diamond | Spade | Heart
type String50 = // non-null, not more than 50 chars
type EmailAddress = // non-null, must contain ‘@’
type StringTransformer = string -> string
type GetCustomer = CustomerId -> Customer option
Instant mockability
![Page 53: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/53.jpg)
Domain modelling pattern: Types are cheap
“There’s no problem that
can’t be solved by
wrapping it in a type”
![Page 54: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/54.jpg)
Types are cheap
type Suit = Club | Diamond | Spade | Hearttype Rank = Two | Three | Four | Five | Six | Seven |
Eight | Nine | Ten | Jack | Queen | King | Ace
type Card = Suit * Ranktype Hand = Card list
New types
composed from
existing types
Only one line of code to create a
type!
![Page 55: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/55.jpg)
Design principle: Strive for totality
![Page 56: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/56.jpg)
twelveDividedBy(x)input x maps to
12/x
…3210…
Domain (int) Codomain (int)…4612…
Totality
int TwelveDividedBy(int input){ switch (input) { case 3: return 4; case 2: return 6; case 1: return 12; case 0: return ??; }}
What happens here?
![Page 57: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/57.jpg)
twelveDividedBy(x)input x maps to
12/x
…321-1…
NonZeroInteger int…4612…
Totality
int TwelveDividedBy(int input){ switch (input) { case 3: return 4; case 2: return 6; case 1: return 12; case -1: return -12; }}
Constrain the input
0 is doesn’t have to be
handledNonZeroInteger -> int
0 is missing
Types are documentation
![Page 58: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/58.jpg)
twelveDividedBy(x)input x maps to
12/x
…3210-1…
int Option<Int>…Some 4Some 6Some 12None…
Totality
int TwelveDividedBy(int input){ switch (input) { case 3: return Some 4; case 2: return Some 6; case 1: return Some 12; case 0: return None; }}
Extend the output
0 is valid input
int -> int option
Types are documentation
![Page 59: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/59.jpg)
Design principle: Use types to indicate errors
![Page 60: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/60.jpg)
Output types as error codes
LoadCustomer: CustomerId -> CustomerLoadCustomer: CustomerId -> SuccessOrFailure<Customer>
ParseInt: string -> intParseInt: string -> int option
FetchPage: Uri -> StringFetchPage: Uri -> SuccessOrFailure<String>
No nullsNo exceptions
Use the signature, Luke!
![Page 61: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/61.jpg)
Domain modelling principle: “Make illegal states unrepresentable”
![Page 62: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/62.jpg)
Types can represent business rules
type EmailContactInfo = | Unverified of EmailAddress | Verified of VerifiedEmailAddress
type ContactInfo = | EmailOnly of EmailContactInfo | AddrOnly of PostalContactInfo | EmailAndAddr of EmailContactInfo * PostalContactInfo
![Page 63: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/63.jpg)
Domain modelling principle: Use sum-types instead of inheritance
![Page 64: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/64.jpg)
Using sum vs. inheritance
interface IPaymentMethod {..}class Cash : IPaymentMethod {..}class Cheque : IPaymentMethod {..}class Card : IPaymentMethod {..}
extra data is obvious
type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber
“closed” set of
options
class Evil : IPaymentMethod {..}
Definition is scattered around many locations
“open” set of options
–unpleasant
surprises?
What goes in here? What is the
common behaviour?
OO version:
![Page 65: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/65.jpg)
Domain modelling principle: Use sum-types for state machines
![Page 66: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/66.jpg)
type ShoppingCart = | EmptyCartState | ActiveCartState of ActiveCartData | PaidCartState of PaidCartData
Empty Cart
Active Cart
Paid Cart
Add Item
Remove Item
Pay
Add Item
Remove Item
![Page 67: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/67.jpg)
Domain modelling principle: It’s ok to expose public data
![Page 68: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/68.jpg)
It’s ok to expose public data
type PersonalName = { FirstName: String50 MiddleInitial: String1 option LastName: String50 }
Immutable
Can’t create invalid values
![Page 69: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/69.jpg)
Domain modelling principle: Types are executable documentation
![Page 70: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/70.jpg)
Types are executable documentation
type Suit = Club | Diamond | Spade | Hearttype Rank = Two | Three | Four | Five | Six | Seven |
Eight | Nine | Ten | Jack | Queen | King | Acetype Card = Suit * Rank
type Hand = Card listtype Deck = Card list
type Player = {Name:string; Hand:Hand}type Game = {Deck:Deck; Players: Player list}
Types can be verbs
Types can be nouns
type Deal = Deck –› (Deck * Card)type PickupCard = (Hand * Card) –› Hand
![Page 71: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/71.jpg)
Types are executable documentation
type CardType = Visa | Mastercardtype CardNumber = CardNumber of stringtype ChequeNumber = ChequeNumber of int
type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber
Can you guess
what payment
methods are
accepted?
![Page 72: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/72.jpg)
More on DDD and designing with types at fsharpforfunandprofit.com/ddd
Static types only!Sorry Clojure and JS developers
![Page 73: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/73.jpg)
HIGH-LEVEL DESIGN
![Page 74: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/74.jpg)
Design paradigm:Functions all the way down
![Page 75: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/75.jpg)
“Functions in the small,
objects in the large”X“Functions in the small,
functions in the large”
![Page 76: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/76.jpg)
Low-level operation
ToUpper
Service
Domain logic
High-level use-case
AddressValidator
VerifyEmailAddress
UpdateProfileData
“Service” is the new “microservice”
string string
AddressResult
EmailVerificationSaga
HttpResponse
Address
HttpReque
st
“Composition is fractal”
![Page 77: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/77.jpg)
Design paradigm:Transformation-oriented programming
![Page 78: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/78.jpg)
Interacting with the outside world
type ContactDTO = {
FirstName: string MiddleInitial: string LastName: string
EmailAddress: string IsEmailVerified: bool }
type EmailAddress = ...
type VerifiedEmail = VerifiedEmail of EmailAddress
type EmailContactInfo = | Unverified of EmailAddress | Verified of VerifiedEmail
type PersonalName = { FirstName: String50 MiddleInitial: String1 option LastName: String50 }
type Contact = { Name: PersonalName Email: EmailContactInfo }
Transformation to clean internal
model
External model
Internal model
![Page 79: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/79.jpg)
Transformation oriented programming
Input Function Output
![Page 80: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/80.jpg)
Transformation oriented programming
Input Transformation to internal model
Internal Model
OutputTransformation from internal model
validation and wrapping happens
here
unwrapping happens here
“transformation-oriented
programming”
![Page 81: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/81.jpg)
Outbound tranformation
Flow of control in a FP use case
Inbound tranformation
Customer
Domain
Validator
Update
Linear!
input output
Request
DTO
Domain Type
Send
ToDTORespon
seDTO
Works well with domain events, FRP, etc
![Page 82: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/82.jpg)
Flow of control in a OO use case
Application Services
Customer
Domain
AppService
AppService
Validation
Value
Entity
Infrastructure
EntityCustomer Repo. Value
EmailMessage
Value
DatabaseService
SMTPService
object
soup!
inputoutpu
t
![Page 83: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/83.jpg)
Interacting with the outside world
Beautiful, clean, internal
model
Nasty, unclean outside world
Nasty, unclean outside world
Nasty, unclean outside world
Beautiful, clean, internal
model
Gate with filters
Gate with filters
Gate with filters
![Page 84: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/84.jpg)
Interacting with the other domains
Subdomain/bounded context
Gate with filters
Subdomain/bounded context
Gate with
filtersTransformation from
one domain to another
![Page 85: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/85.jpg)
Interacting with the other domains
Hexagonal architecture!
Bounded context
Bounded context
Bounded context
![Page 86: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/86.jpg)
FUNCTIONS AS PARAMETERS
![Page 87: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/87.jpg)
Guideline: Parameterize all the things
![Page 88: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/88.jpg)
Parameterize all the things
let printList() = for i in [1..10] do printfn "the number is %i" i
![Page 89: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/89.jpg)
Parameterize all the things
It's second nature to parameterize the data input:let printList aList = for i in aList do printfn "the number is %i" i
![Page 90: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/90.jpg)
Parameterize all the things
let printList anAction aList = for i in aList do anAction i
FPers would parameterize the action as well:
A good language
makes this trivial
to do!
We've decoupled the behavior from
the data
![Page 91: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/91.jpg)
Parameterize all the thingspublic static int Product(int n){ int product = 1; for (int i = 1; i <= n; i++) { product *= i; } return product;}
public static int Sum(int n){ int sum = 0; for (int i = 1; i <= n; i++) { sum += i; } return sum;}
Don't Repeat
Yourself
![Page 92: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/92.jpg)
public static int Product(int n){ int product = 1; for (int i = 1; i <= n; i++) { product *= i; } return product;}
public static int Sum(int n){ int sum = 0; for (int i = 1; i <= n; i++) { sum += i; } return sum;}
Parameterize all the things
Action
Initial Value
Common Code
![Page 93: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/93.jpg)
Parameterize all the thingslet product n = let initialValue = 1 let action productSoFar x = productSoFar * x [1..n] |> List.fold action initialValue
let sum n = let initialValue = 0 let action sumSoFar x = sumSoFar+x [1..n] |> List.fold action initialValue
Parameterized
actionCommon code
extracted
Initial Value
Lots of collection functions like this:
"fold", "map", "reduce", "collect", etc.
![Page 94: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/94.jpg)
Guideline: Be as generic as possible
![Page 95: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/95.jpg)
Generic code
let printList anAction aList = for i in aList do anAction i
// val printList : // ('a -> unit) -> seq<'a> -> unit
Any kind of collection, any kind of action!
F# and other functional languages make code generic automatically
![Page 96: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/96.jpg)
Generic code
int -> int
How many ways are there to implement
this function?'a -> 'a
How many ways are there to this function?
![Page 97: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/97.jpg)
Generic code
int list -> int list
How many ways are there to implement
this function?'a list -> 'a list
How many ways are there to this function?
![Page 98: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/98.jpg)
Generic code
('a -> 'b) -> 'a list -> 'b list
How many ways are there to implement
this function?
![Page 99: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/99.jpg)
Tip: Function types are
"interfaces"
Function types provide instant abstraction!
![Page 100: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/100.jpg)
Function types are interfaces
interface IBunchOfStuff{ int DoSomething(int x); string DoSomethingElse(int x); void DoAThirdThing(string x);}
Let's take the Single Responsibility Principle
and the Interface Segregation Principle
to the extreme...Every interface should have
only one method!
![Page 101: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/101.jpg)
Function types are interfaces
interface IBunchOfStuff{ int DoSomething(int x);}
An interface with one method is a just a function typetype IBunchOfStuff: int -> int
Any function with that type is compatible with itlet add2 x = x + 2 // int -> intlet times3 x = x * 2 // int -> int
![Page 102: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/102.jpg)
Strategy pattern is trivial in FP
class MyClassinterface IBunchOfStuff{ public MyClass(IBunchOfStuff strategy) {..}
int DoSomethingWithStuff(int x) { return _strategy.DoSomething(x) }}
Object-oriented strategy pattern:
Functional equivalent:
let DoSomethingWithStuff strategy x = strategy x
+ with FP approach =>you don't need to create an
interface in advance.& you can substitute ANY function in later
![Page 103: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/103.jpg)
Decorator pattern in FP
Functional equivalent of decorator pattern
let add1 x = x + 1 // int -> int
![Page 104: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/104.jpg)
Decorator pattern in FP
Functional equivalent of decorator pattern
let add1 x = x + 1 // int -> int
let logged f x = printfn "input is %A" x let result = f x printfn "output is %A" result result
![Page 105: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/105.jpg)
Decorator pattern in FP
Functional equivalent of decorator pattern
let add1 x = x + 1 // int -> int
let logged f x = printfn "input is %A" x let result = f x printfn "output is %A" result result
let add1Decorated = // int -> int logged add1
[1..5] |> List.map add1[1..5] |> List.map add1Decorated
![Page 106: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/106.jpg)
Tip Every function is a
one parameter function
![Page 107: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/107.jpg)
Writing functions in different ways
let add x y = x + y
let add = (fun x y -> x + y)
let add x = (fun y -> x + y)
int-> int->int
int-> int->int
int-> (int->int)
Two parameters
No parameters
One parameter
![Page 108: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/108.jpg)
let three = 1 + 2
let add1 = (+) 1
let three = (+) 1 2
let add1ToEach = List.map add1
Missing a
parameter?
![Page 109: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/109.jpg)
Pattern: Partial application
![Page 110: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/110.jpg)
let names = ["Alice"; "Bob"; "Scott"]Names |> List.iter hello
let name = "Scott"printfn "Hello, my name is %s" name
let name = "Scott"(printfn "Hello, my name is %s") name
let name = "Scott"let hello = (printfn "Hello, my name is %s")hello name
Two parameters
One parameter
![Page 111: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/111.jpg)
Pattern: Use partial application to do
dependency injection
![Page 112: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/112.jpg)
type GetCustomer = CustomerId -> Customer
let getCustomerFromDatabase connection (customerId:CustomerId) = // from connection // select customer // where customerId = customerId
type of getCustomerFromDatabase = DbConnection -> CustomerId -> Customer
let getCustomer1 = getCustomerFromDatabase myConnection
// getCustomer1 : CustomerId -> Customer
Persistence agnostic
![Page 113: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/113.jpg)
type GetCustomer = CustomerId -> Customer
let getCustomerFromMemory map (customerId:CustomerId) = map |> Map.find customerId
type of getCustomerFromMemory = Map<Id,Customer> -> CustomerId -> Customer
let getCustomer2 = getCustomerFromMemory inMemoryMap
// getCustomer2 : CustomerId -> Customer
![Page 114: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/114.jpg)
Pattern: The Hollywood principle:
continuations
![Page 115: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/115.jpg)
Continuations
int Divide(int top, int bottom) { if (bottom == 0) { throw new InvalidOperationException("div by 0"); } else { return top/bottom; }}
Method has decided to throw an exception
![Page 116: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/116.jpg)
Continuations
void Divide(int top, int bottom, Action ifZero, Action<int> ifSuccess) { if (bottom == 0) { ifZero(); } else { ifSuccess( top/bottom ); } }
Let the caller decide what happens
what happens next?
![Page 117: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/117.jpg)
Continuations
let divide ifZero ifSuccess top bottom = if (bottom=0) then ifZero() else ifSuccess (top/bottom)
F# version
Four parameters is a lot though!
![Page 118: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/118.jpg)
Continuations
let divide ifZero ifSuccess top bottom = if (bottom=0) then ifZero() else ifSuccess (top/bottom)
let ifZero1 () = printfn "bad"let ifSuccess1 x = printfn "good %i" x
let divide1 = divide ifZero1 ifSuccess1
//testlet good1 = divide1 6 3let bad1 = divide1 6 0
setup the functions to print
a message
Partially apply the continuations
Use it like a normal function – only two
parameters
![Page 119: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/119.jpg)
Continuations
let divide ifZero ifSuccess top bottom = if (bottom=0) then ifZero() else ifSuccess (top/bottom)
let ifZero2() = Nonelet ifSuccess2 x = Some x
let divide2 = divide ifZero2 ifSuccess2
//testlet good2 = divide2 6 3let bad2 = divide2 6 0
setup the functions to return
an Option
Use it like a normal function – only two
parameters
Partially apply the continuations
![Page 120: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/120.jpg)
Continuations
let divide ifZero ifSuccess top bottom = if (bottom=0) then ifZero() else ifSuccess (top/bottom)
let ifZero3() = failwith "div by 0"let ifSuccess3 x = x
let divide3 = divide ifZero3 ifSuccess3
//testlet good3 = divide3 6 3let bad3 = divide3 6 0
setup the functions to throw
an exception
Use it like a normal function – only two
parameters
Partially apply the continuations
![Page 121: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/121.jpg)
MONADS
![Page 122: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/122.jpg)
Pyramid of doom: null testing example
let example input = let x = doSomething input if x <> null then let y = doSomethingElse x if y <> null then let z = doAThirdThing y if z <> null then let result = z result else null else null else null
Nested null
checks
I know you could do early returns, but bear
with me...
The pyramid of doom
![Page 123: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/123.jpg)
Pyramid of doom: async example
let taskExample input = let taskX = startTask input taskX.WhenFinished (fun x -> let taskY = startAnotherTask x taskY.WhenFinished (fun y -> let taskZ = startThirdTask y taskZ.WhenFinished (fun z -> z // final result
Nested callbacks
![Page 124: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/124.jpg)
Pyramid of doom: null example
let example input = let x = doSomething input if x <> null then let y = doSomethingElse x if y <> null then let z = doAThirdThing y if z <> null then let result = z result else null else null else null
Nulls are a code smell:replace with Option!
![Page 125: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/125.jpg)
Pyramid of doom: option example
let example input = let x = doSomething input if x.IsSome then let y = doSomethingElse (x.Value) if y.IsSome then let z = doAThirdThing (y.Value) if z.IsSome then let result = z.Value Some result else None else None else None
Much more elegant, yes?
No! This is fugly!Let’s do a cut & paste
refactoring
![Page 126: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/126.jpg)
Pyramid of doom: option example
let example input = let x = doSomething input if x.IsSome then let y = doSomethingElse (x.Value) if y.IsSome then let z = doAThirdThing (y.Value) if z.IsSome then let result = z.Value Some result else None else None else None
![Page 127: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/127.jpg)
Pyramid of doom: option example
let doWithX x = let y = doSomethingElse x if y.IsSome then let z = doAThirdThing (y.Value) if z.IsSome then let result = z.Value Some result else None else None
let example input = let x = doSomething input if x.IsSome then doWithX x else None
![Page 128: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/128.jpg)
Pyramid of doom: option example
let doWithX x = let y = doSomethingElse x if y.IsSome then let z = doAThirdThing (y.Value) if z.IsSome then let result = z.Value Some result else None else None
let example input = let x = doSomething input if x.IsSome then doWithX x else None
![Page 129: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/129.jpg)
Pyramid of doom: option example
let doWithY y = let z = doAThirdThing y if z.IsSome then let result = z.Value Some result else None
let doWithX x = let y = doSomethingElse x if y.IsSome then doWithY y else None
let example input = let x = doSomething input if x.IsSome then doWithX x else None
![Page 130: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/130.jpg)
Pyramid of doom: option example refactored
let doWithZ z = let result = z Some result
let doWithY y = let z = doAThirdThing y if z.IsSome then doWithZ z.Value else None
let doWithX x = let y = doSomethingElse x if y.IsSome then doWithY y.Value else None
let optionExample input = let x = doSomething input if x.IsSome then doWithX x.Value else None
Three small pyramids instead
of one big one!This is still ugly!
But the code has a pattern...
![Page 131: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/131.jpg)
Pyramid of doom: option example refactored
let doWithZ z = let result = z Some result
let doWithY y = let z = doAThirdThing y if z.IsSome then doWithZ z.Value else None
let doWithX x = let y = doSomethingElse x if y.IsSome then doWithY y.Value else None
let optionExample input = let x = doSomething input if x.IsSome then doWithX x.Value else None
But the code has a pattern...
![Page 132: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/132.jpg)
let doWithY y = let z = doAThirdThing y if z.IsSome then doWithZ z.Value else None Crying out to be
parameterized!
![Page 133: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/133.jpg)
let doWithY y = let z = doAThirdThing y z |> ifSomeDo doWithZ
let ifSomeDo f x = if x.IsSome then f x.Value else None
![Page 134: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/134.jpg)
let doWithY y = y |> doAThirdThing |> ifSomeDo doWithZ
let ifSomeDo f x = if x.IsSome then f x.Value else None
![Page 135: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/135.jpg)
let example input = doSomething input |> ifSomeDo doSomethingElse |> ifSomeDo doAThirdThing |> ifSomeDo (fun z -> Some z)
let ifSomeDo f x = if x.IsSome then f x.Value else None
![Page 136: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/136.jpg)
A switch analogy
Some
None
Input ->
![Page 137: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/137.jpg)
Connecting switches
on Some
Bypass on None
![Page 138: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/138.jpg)
Connecting switches
![Page 139: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/139.jpg)
Connecting switches
![Page 140: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/140.jpg)
Composing switches
>> >>
Composing one-track functions is fine...
![Page 141: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/141.jpg)
Composing switches
>> >>
... and composing two-track functions is fine...
![Page 142: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/142.jpg)
Composing switches
... but composing switches is not allowed!
![Page 143: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/143.jpg)
Composing switches
Two-track input Two-track input
One-track input Two-track input
Before: Not suitable for composition
After: Suitable for composition
So how can we convert from the
"before" case to the "after" case?
![Page 144: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/144.jpg)
Building an adapter block
Two-track input
Slot for switch function
Two-track output
![Page 145: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/145.jpg)
Building an adapter block
Two-track input Two-track output
![Page 146: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/146.jpg)
let bind nextFunction optionInput = match optionInput with | Some s -> nextFunction s | None -> None
Building an adapter block
Two-track input Two-track output
![Page 147: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/147.jpg)
let bind nextFunction optionInput = match optionInput with | Some s -> nextFunction s | None -> None
Building an adapter block
Two-track input Two-track output
![Page 148: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/148.jpg)
let bind nextFunction optionInput = match optionInput with | Some s -> nextFunction s | None -> None
Building an adapter block
Two-track input Two-track output
This is a continuati
on
![Page 149: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/149.jpg)
let bind nextFunction optionInput = match optionInput with | Some s -> nextFunction s | None -> None
Building an adapter block
Two-track input Two-track output
![Page 150: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/150.jpg)
Pattern: Use bind to chain options
![Page 151: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/151.jpg)
Pyramid of doom: using bind
let bind f opt = match opt with | Some v -> f v | None -> None
let example input = let x = doSomething input if x.IsSome then let y = doSomethingElse (x.Value) if y.IsSome then let z = doAThirdThing (y.Value) if z.IsSome then let result = z.Value Some result else None else None else None
![Page 152: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/152.jpg)
let example input = doSomething input |> bind doSomethingElse |> bind doAThirdThing |> bind (fun z -> Some z)
Pyramid of doom: using bind
let bind f opt = match opt with | Some v -> f v | None -> None
No pyramids!Code is linear and
clear.This pattern is called
“monadic bind”
![Page 153: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/153.jpg)
Pattern: Use bind to chain tasks
![Page 154: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/154.jpg)
Connecting tasks
When task completes
Wait
Wait
![Page 155: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/155.jpg)
Pyramid of doom: using bind for tasks
let taskBind f task = task.WhenFinished (fun taskResult -> f taskResult)
let taskExample input = startTask input |> taskBind startAnotherTask |> taskBind startThirdTask |> taskBind (fun z -> z)
a.k.a “promise” “future”
This pattern is also a “monadic bind”
![Page 156: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/156.jpg)
Computation expressions
let example input = maybe { let! x = doSomething input let! y = doSomethingElse x let! z = doAThirdThing y return z }
let taskExample input = task { let! x = startTask input let! y = startAnotherTask x let! z = startThirdTask z return z }
Computation expression
Computation expression
![Page 157: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/157.jpg)
Pattern: Use bind to chain error
handlers
![Page 158: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/158.jpg)
Example use case
Name is blankEmail not valid
Receive request
Validate and canonicalize request
Update existing user record
Send verification email
Return result to user
User not foundDb error
Authorization errorTimeout
"As a user I want to update my name and email address"
type Request = { userId: int; name: string; email: string }
- and see sensible error messages when something goes wrong!
![Page 159: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/159.jpg)
Use case without error handlingstring UpdateCustomerWithErrorHandling() { var request = receiveRequest(); validateRequest(request); canonicalizeEmail(request); db.updateDbFromRequest(request); smtpServer.sendEmail(request.Email)
return "OK";}
![Page 160: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/160.jpg)
Use case with error handlingstring UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid" } canonicalizeEmail(request); db.updateDbFromRequest(request); smtpServer.sendEmail(request.Email)
return "OK";}
![Page 161: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/161.jpg)
Use case with error handlingstring UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid" } canonicalizeEmail(request); var result = db.updateDbFromRequest(request); if (!result) { return "Customer record not found" }
smtpServer.sendEmail(request.Email)
return "OK";}
![Page 162: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/162.jpg)
Use case with error handlingstring UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid" } canonicalizeEmail(request); try { var result = db.updateDbFromRequest(request); if (!result) { return "Customer record not found" } } catch { return "DB error: Customer record not updated" }
smtpServer.sendEmail(request.Email)
return "OK";}
![Page 163: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/163.jpg)
Use case with error handlingstring UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid" } canonicalizeEmail(request); try { var result = db.updateDbFromRequest(request); if (!result) { return "Customer record not found" } } catch { return "DB error: Customer record not updated" }
if (!smtpServer.sendEmail(request.Email)) { log.Error "Customer email not sent" }
return "OK";}
![Page 164: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/164.jpg)
Use case with error handlingstring UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid" } canonicalizeEmail(request); try { var result = db.updateDbFromRequest(request); if (!result) { return "Customer record not found" } } catch { return "DB error: Customer record not updated" }
if (!smtpServer.sendEmail(request.Email)) { log.Error "Customer email not sent" }
return "OK";}
6 clean lines -> 18 ugly lines. 200% extra!
Sadly this is typical of error handling code.
![Page 165: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/165.jpg)
A structure for managing errors
Request SuccessValidateFailure
let validateInput input = if input.name = "" then Failure "Name must not be blank" else if input.email = "" then Failure "Email must not be blank" else Success input // happy path
type TwoTrack<'TEntity> = | Success of 'TEntity | Failure of string
![Page 166: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/166.jpg)
name50
Bind example
let nameNotBlank input = if input.name = "" then Failure "Name must not be blank" else Success input
let name50 input = if input.name.Length > 50 then Failure "Name must not be longer than 50 chars" else Success input
let emailNotBlank input = if input.email = "" then Failure "Email must not be blank" else Success input
nameNotBlank
emailNotBlank
![Page 167: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/167.jpg)
Switches again
Success!Failure
Input ->
![Page 168: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/168.jpg)
Connecting switches
Validate UpdateDb
on success
bypass
![Page 169: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/169.jpg)
Connecting switches
Validate UpdateDb
![Page 170: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/170.jpg)
Connecting switches
Validate UpdateDb SendEmail
![Page 171: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/171.jpg)
Connecting switches
Validate UpdateDb SendEmail
This is the "two track" model –
the basis for the "Railway Oriented
Programming" approach to error handling.
![Page 172: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/172.jpg)
Functional flow without error handling
let updateCustomer = receiveRequest |> validateRequest |> canonicalizeEmail |> updateDbFromRequest |> sendEmail |> returnMessage
Before
One track
![Page 173: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/173.jpg)
Functional flow with error handling
let updateCustomerWithErrorHandling = receiveRequest |> validateRequest |> canonicalizeEmail |> updateDbFromRequest |> sendEmail |> returnMessage
After
See fsharpforfunandprofit.com/rop
Two track
![Page 174: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/174.jpg)
MAPS AND APPLICATIVES
![Page 175: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/175.jpg)
World of normal values
int string bool
World of options
int option string option bool option
![Page 176: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/176.jpg)
World of options
World of normal values
int string bool
int option string option bool option
![Page 177: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/177.jpg)
World of options
World of normal values
int string bool
int option string option bool option
![Page 178: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/178.jpg)
How not to code with options
Let’s say you have an int wrapped in an Option, and you want to add 42 to it:
let add42 x = x + 42
let add42ToOption opt = if opt.IsSome then let newVal = add42 opt.Value Some newVal else None
Unwrap
Wrap again
Works on normal values
![Page 179: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/179.jpg)
World of options
World of normal values
add42
Unwrap Wrap
![Page 180: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/180.jpg)
World of options
World of normal values
add42
But how?
![Page 181: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/181.jpg)
Lifting
World of options
World of normal values
'a -> 'b
'a option -> 'b option
Option.map
![Page 182: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/182.jpg)
The right way to code with options
Let’s say you have an int wrapped in an Option, and you want to add 42 to it:
let add42 x = x + 42
let add42ToOption = Option.map add42Some 1 |> add42ToOption Some 1 |> Option.map add42
Often no need to
bother creating a
temp function
![Page 183: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/183.jpg)
Pattern: Use “map” to lift functions
![Page 184: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/184.jpg)
Lifting to lists
World of lists
World of normal values
'a -> 'b
'a list-> 'b list
List.map
![Page 185: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/185.jpg)
Lifting to async
World of async
World of normal values
'a -> 'b
'a async > 'b async
Async.map
![Page 186: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/186.jpg)
The right way to code with wrapped types
Most wrapped types provide a “map”
let add42 x = x + 42
Some 1 |> Option.map add42
[1;2;3] |> List.map add42
![Page 187: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/187.jpg)
Guideline: If you create a wrapped generic type,
create a “map” for it.
Terminology alert: Mappable types are
“functors”
![Page 188: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/188.jpg)
Maps
type TwoTrack<'TEntity> = | Success of 'TEntity | Failure of string
let mapTT f x = match x with | Success entity -> Success (f entity) | Failure s -> Failure s
My Map!
![Page 189: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/189.jpg)
Tip: Use applicatives for validation
What’s an applicative?
![Page 190: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/190.jpg)
Series validation
name50 emailNotBlank
Problem: Validation done in series.
So only one error at a time is returned
It would be nice to return all validation
errors at once.
![Page 191: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/191.jpg)
Parallel validation
name50
emailNotBlank
Split input
Combine output
Now we do get all errors at
once!
But how to combine?
![Page 192: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/192.jpg)
Creating a valid data structure
type Request= { UserId: UserId; Name: String50; Email: EmailAddress}
type RequestDTO= { UserId: int; Name: string; Email: string}
These need validation
These are “pure”
Domain object
Object from the
outside
![Page 193: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/193.jpg)
How not to do validation
// do the validation of the DTOlet userIdOrError = validateUserId dto.userIdlet nameOrError = validateName dto.namelet emailOrError = validateEmail dto.email
if userIdOrError.IsSuccess && nameOrError.IsSuccess && emailOrError.IsSuccess then { userId = userIdOrError.SuccessValue name = nameOrError.SuccessValue email = emailOrError.SuccessValue }else if userIdOrError.IsFailure && nameOrError.IsSuccess && emailOrError.IsSuccess then userIdOrError.Errorselse ...
No no no this is
horrible!
![Page 194: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/194.jpg)
Lifting to TwoTracks
World of two-tracks
World of normal values
createRequest userId name email
createRequestTT userIdOrError nameOrError emailOrError
lift 3 parameter function
![Page 195: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/195.jpg)
The right way
let createRequest userId name email = { userId = userIdOrError.SuccessValue name = nameOrError.SuccessValue email = emailOrError.SuccessValue }
let createRequestTT = lift3 createRequest
There is a separate “lift” function
for each number of parameters: “lift2” “lift3” etc.
“lift1” is just “map”
First create a function that creates a Request
then "lift" it to the world of two track errors
![Page 196: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/196.jpg)
The right way
let createRequestTT = lift3 createRequest
let userIdOrError = validateUserId dto.userIdlet nameOrError = validateName dto.namelet emailOrError = validateEmail dto.email
let requestOrError = createRequestTT userIdOrError nameOrError emailOrError
Now you can pass in the errors safely
The result is also in the world of two
tracks
![Page 197: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/197.jpg)
The right way
let userIdOrError = validateUserId dto.userIdlet nameOrError = validateName dto.namelet emailOrError = validateEmail dto.email
let requestOrError = createRequest <!> userIdOrError <*> nameOrError <*> emailOrError
Alternative using <!> and <*>
![Page 198: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/198.jpg)
Guideline: If you use a wrapped generic type, look
for a set of “lifts” associated with it
Terminology alert: Liftable types are “applicative
functors”
![Page 199: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/199.jpg)
Guideline: If you create a wrapped generic type, also
create a set of “lifts” for clients to use with it
But I’m not going explain how right now!
![Page 200: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/200.jpg)
MONOIDS
![Page 201: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/201.jpg)
MathematicsAhead
![Page 202: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/202.jpg)
Thinking like a mathematician
1 + 2 = 3
1 + (2 + 3) = (1 + 2) + 3
1 + 0 = 1 0 + 1 = 1
![Page 203: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/203.jpg)
1 + 2 = 3
Some things
A way of combining
them
Another one
of those things
![Page 204: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/204.jpg)
1 x 2 = 3
Some things
A way of combining
them
Another one
of those things
![Page 205: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/205.jpg)
max(1,2) = 2
Some things
A way of combining
them
Another one
of those things
![Page 206: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/206.jpg)
"a" + "b" = "ab"
Some things
A way of combining
them
Another one
of those things
![Page 207: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/207.jpg)
concat([a],[b]) = [a; b]
Some things
A way of combining
them
Another one
of those things
![Page 208: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/208.jpg)
1 + 2
1 + 2 + 31 + 2 + 3 + 4
Is an integer
Is an integer
A pairwise operation has become an
operation that works on lists!
![Page 209: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/209.jpg)
1 + (2 + 3) = (1 + 2) + 3
Order of combining doesn’t matter
1 + 2 + 3 + 4 (1 + 2) + (3 + 4)((1 + 2) + 3) + 4
All the same
![Page 210: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/210.jpg)
1 - (2 - 3) = (1 - 2) - 3
Order of combining does matter
![Page 211: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/211.jpg)
1 + 0 = 1 0 + 1 = 1
A special kind of thing that when you combine it
with something, just gives you back the original something
![Page 212: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/212.jpg)
42 * 1 = 42 1 * 42 = 42
A special kind of thing that when you combine it
with something, just gives you back the original something
![Page 213: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/213.jpg)
"" + "hello" = "hello" "hello" + "" = "hello"
“Zero” for strings
![Page 214: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/214.jpg)
The generalization
• You start with a bunch of things, and some way of combining them two at a time.
• Rule 1 (Closure): The result of combining two things is always another one of the things.
• Rule 2 (Associativity): When combining more than two things, which pairwise combination you do first doesn't matter.
• Rule 3 (Identity element): There is a special thing called "zero" such that when you combine any thing with "zero" you get the original thing back.
A monoid!
![Page 215: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/215.jpg)
• Rule 1 (Closure): The result of combining two things is always another one of the things.
• Benefit: converts pairwise operations into operations that work on lists.
1 + 2 + 3 + 4[ 1; 2; 3; 4 ] |> List.reduce (+)
![Page 216: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/216.jpg)
1 * 2 * 3 * 4[ 1; 2; 3; 4 ] |> List.reduce (*)
• Rule 1 (Closure): The result of combining two things is always another one of the things.
• Benefit: converts pairwise operations into operations that work on lists.
![Page 217: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/217.jpg)
"a" + "b" + "c" + "d"[ "a"; "b"; "c"; "d" ] |> List.reduce (+)
• Rule 1 (Closure): The result of combining two things is always another one of the things.
• Benefit: converts pairwise operations into operations that work on lists.
![Page 218: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/218.jpg)
• Rule 2 (Associativity): When combining more than two things, which pairwise combination you do first doesn't matter.
• Benefit: Divide and conquer, parallelization, and incremental accumulation.1 + 2 + 3 + 4
![Page 219: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/219.jpg)
• Rule 2 (Associativity): When combining more than two things, which pairwise combination you do first doesn't matter.
• Benefit: Divide and conquer, parallelization, and incremental accumulation.(1 + 2) (3 + 4) 3 + 7
![Page 220: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/220.jpg)
• Rule 2 (Associativity): When combining more than two things, which pairwise combination you do first doesn't matter.
• Benefit: Divide and conquer, parallelization, and incremental accumulation.(1 + 2 + 3)
![Page 221: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/221.jpg)
• Rule 2 (Associativity): When combining more than two things, which pairwise combination you do first doesn't matter.
• Benefit: Divide and conquer, parallelization, and incremental accumulation.(1 + 2 + 3) + 4
![Page 222: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/222.jpg)
• Rule 2 (Associativity): When combining more than two things, which pairwise combination you do first doesn't matter.
• Benefit: Divide and conquer, parallelization, and incremental accumulation.(6) + 4
![Page 223: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/223.jpg)
Issues with reduce
• How can I use reduce on an empty list?
• In a divide and conquer algorithm, what should I do if one of the "divide" steps has nothing in it?
• When using an incremental algorithm, what value should I start with when I have no data?
![Page 224: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/224.jpg)
• Rule 3 (Identity element): There is a special thing called "zero" such that when you combine any thing with "zero" you get the original thing back.
• Benefit: Initial value for empty or missing data
If missing,
it ;s called a semigrou
p
![Page 225: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/225.jpg)
Pattern: Simplifying aggregation code with
monoids
![Page 226: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/226.jpg)
type OrderLine = {Qty:int; Total:float}
let orderLines = [ {Qty=2; Total=19.98} {Qty=1; Total= 1.99} {Qty=3; Total= 3.99} ]
let addLine line1 line2 = let newQty = line1.Qty + line2.Qty let newTotal = line1.Total + line2.Total {Qty=newQty; Total=newTotal}
orderLines |> List.reduce addLine
Any combination of monoids
is also a monoid
![Page 227: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/227.jpg)
Pattern: Convert non-monoids to monoids
![Page 228: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/228.jpg)
Customer+
Customer+
Customer
Customer Stats+
Customer Stats+
Customer Stats
Reduce
Map
Map/Reduce!
Not a monoid
A monoid
Summary Stats
![Page 229: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/229.jpg)
Hadoop make me a sandwich
https://twitter.com/daviottenheimer/status/532661754820829185
![Page 230: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/230.jpg)
Guideline: Convert expensive monoids
to cheap monoids
![Page 231: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/231.jpg)
Log file (Mon)+
Log File (Tue)+
Log File (Wed)
=Really big file
Summary (Mon)+
Summary (Tue)+
Summary (Wed)
Map
Strings are monoids
A monoid
Much more efficient for incremental
updates“Monoid
homomorphism”
![Page 232: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/232.jpg)
Pattern: Seeing monoids everywhere
![Page 233: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/233.jpg)
Monoids in the real world
Metrics guideline: Use counters rather than rates
Alternative metrics guideline: Make sure your metrics are monoids• incremental updates• can handle missing data
![Page 234: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/234.jpg)
Is function composition a monoid?
>>Function 1apple -> banana
Function 2banana -> cherry
New Functionapple -> cherry
Not the same thing.
Not a monoid
![Page 235: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/235.jpg)
Is function composition a monoid?
>>Function 1apple -> apple
Same thing
Function 2apple -> apple
Function 3apple -> apple
A monoid!
![Page 236: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/236.jpg)
Is function composition a monoid?
“Functions with same type of input and output”
Functions where the input and output are the same type are monoids! What shall we call these kinds of functions?
![Page 237: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/237.jpg)
Is function composition a monoid?
“Functions with same type of input and output”“Endomorphisms”
Functions where the input and output are the same type are monoids! What shall we call these kinds of functions?
All endomorphisms are monoids
![Page 238: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/238.jpg)
Endomorphism example
let plus1 x = x + 1 // int->intlet times2 x = x * 2 // int->intlet subtract42 x = x – 42 // int->int
let functions = [ plus1 times2 subtract42 ]
let newFunction = // int->int functions |> List.reduce (>>)
newFunction 20 // => 0
Endomorphisms
Put them in a list
Reduce them
Another endomorph
ism
![Page 239: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/239.jpg)
Event sourcing
Any function containing an endomorphism can be converted into a monoid.
For example: Event sourcing
Is an endomorphism
Event-> State-> State
![Page 240: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/240.jpg)
Event sourcing example
let applyFns = [ apply event1 // State -> State apply event2 // State -> State apply event3] // State -> State
let applyAll = // State -> State applyFns |> List.reduce (>>)
let newState = applyAll oldState
• incremental updates• can handle missing events
Partial application of event
A function that can apply all the events in one step
![Page 241: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/241.jpg)
Bind
Any function containing an endomorphism can be converted into a monoid.
For example: Option.Bind
Is an endomorphism
(fn param)-> Option-> Option
![Page 242: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/242.jpg)
Event sourcing example
let bindFns = [ Option.bind (fun x-> if x > 1 then Some (x*2) else None) Option.bind (fun x-> if x < 10 then Some x else None) ]
let bindAll = // Option->Option bindFns |> List.reduce (>>)
Some 4 |> bindAll // Some 8Some 5 |> bindAll // None
Partial application of Bind
![Page 243: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/243.jpg)
Predicates as monoids
type Predicate<'A> = 'A -> bool
let predAnd pred1 pred2 x = if pred1 x then pred2 x else false
let predicates = [ isMoreThan10Chars // string -> bool isMixedCase // string -> bool isNotDictionaryWord // string -> bool ]
let combinedPredicate = // string -> bool predicates |> List.reduce (predAnd)
Not an endomorphism
But can still be combined
![Page 244: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/244.jpg)
Pattern: Monads are monoids
![Page 245: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/245.jpg)
Series combination
=>> Result is
same kind of thing
(Closure)
Order not important
(Associative) Monoid
!
![Page 246: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/246.jpg)
Parallel combination
=+Same thing (Closure)
Order not important
(Associative)
Monoid!
![Page 247: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/247.jpg)
Monad laws
• The Monad laws are just the monoid definitions in diguise– Closure, Associativity, Identity
• What happens if you break the monad laws?– You lose monoid benefits such as
aggregation
![Page 248: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/248.jpg)
A monad is just a monoid in the category of
endofunctors!
![Page 249: Functional Design Patterns @ScottWlaschin fsharpforfunandprofit.com.](https://reader035.fdocuments.net/reader035/viewer/2022062320/56649f515503460f94c74b2f/html5/thumbnails/249.jpg)
THANKS! More F# here!