functional patterns
DESCRIPTION
Listing patterns w.r.t functional programming.TRANSCRIPT
![Page 1: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/1.jpg)
Functional Design Patterns (NDC London 2014)
@ScottWlaschin
fsharpforfunandprofit.com
![Page 2: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/2.jpg)
Functional Design Patterns
@ScottWlaschin
fsharpforfunandprofit.com
X
![Page 3: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/3.jpg)
Functional Design Patterns
@ScottWlaschin
fsharpforfunandprofit.com
![Page 4: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/4.jpg)
HOW I GOT HERE
![Page 5: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/5.jpg)
Me
![Page 6: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/6.jpg)
I
![Page 7: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/7.jpg)
Making fun of Enterprise OO is like shooting fish in a barrel
I but...
![Page 8: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/8.jpg)
I but...
Making fun of Enterprise OO is like shooting IMarineVertebrates in an AbstractBarrelProxyFactory
Inspired by @sjkilleen
![Page 9: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/9.jpg)
I
![Page 10: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/10.jpg)
This is what happens when you dabble too much in functional programming
![Page 11: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/11.jpg)
https://www.flickr.com/photos/37511372@N08/5488671752/
Haskell programmers
F#/OCaml programmers
Visual Basic programmers
Lisp programmers
![Page 12: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/12.jpg)
FP DESIGN PATTERNS
![Page 13: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/13.jpg)
• Single Responsibility Principle
• Open/Closed principle
• Dependency Inversion
Principle
• Interface Segregation
Principle
• Factory pattern
• Strategy pattern
• Decorator pattern
• Visitor pattern
OO pattern/principle
![Page 14: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/14.jpg)
• Single Responsibility Principle
• Open/Closed principle
• Dependency Inversion
Principle
• Interface Segregation
Principle
• Factory pattern
• Strategy pattern
• Decorator pattern
• Visitor pattern
OO pattern/principle Borg response
![Page 15: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/15.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
• You will be assimilated!
• Functions again
• Functions
• Resistance is futile!
OO pattern/principle FP equivalent
Seriously, FP patterns are different
![Page 16: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/16.jpg)
Functional patterns
• Apomorphisms
• Dynamorphisms
• Chronomorphisms
• Zygohistomorphic prepromorphisms
![Page 17: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/17.jpg)
Functional patterns
• Core Principles of FP design
– Functions, types, composition
• Functions as parameters
– Functions as interfaces
– Partial application & dependency injection
– Continuations, chaining & the pyramid of doom
• Monads
– Error handling, Async
• Maps
– Dealing with wrapped data
– Functors
• Monoids
– Aggregating data and operations
![Page 18: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/18.jpg)
This talk
A whirlwind tour of many sights Don't worry if you don't understand everything
![Page 19: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/19.jpg)
(SOME) CORE PRINCIPLES OF
FUNCTIONAL PROGRAMMING
Important to understand!
![Page 20: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/20.jpg)
Core principles of FP
Function
Types are not classes
Functions are things
Composition everywhere
![Page 21: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/21.jpg)
Core principle:
Functions are things
Function
![Page 22: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/22.jpg)
Functions as things
The Tunnel of Transformation Function
apple -> banana
A function is a standalone thing, not attached to a class
![Page 23: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/23.jpg)
Functions as things
let z = 1
int-> int->int
1
let add x y = x + y
![Page 24: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/24.jpg)
Functions as inputs and outputs
let useFn f = (f 1) + 2
let add x = (fun y -> x + y)
int->(int->int)
int
int
let transformInt f x = (f x) + 1
int int
int->int
Function as output
Int ->int
Function as input
Function as parameter int->int
(int->int)->int
int->int
![Page 25: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/25.jpg)
Core principle:
Composition everywhere
![Page 26: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/26.jpg)
Function composition
Function 1
apple -> banana
Function 2
banana -> cherry
![Page 27: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/27.jpg)
Function composition
>> Function 1
apple -> banana Function 2
banana -> cherry
![Page 28: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/28.jpg)
Function composition
New Function
apple -> cherry
Can't tell it was built from smaller functions!
![Page 29: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/29.jpg)
Design paradigm:
Functions all the way down
![Page 30: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/30.jpg)
“Functions in the small,
objects in the large”
![Page 31: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/31.jpg)
Low-level operation
ToUpper string string
![Page 32: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/32.jpg)
Low-level operation
Service
AddressValidator
A “Service” is just like a microservice but without the "micro" in front
Validation
Result
Address
Low-level operation Low-level operation
![Page 33: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/33.jpg)
Service
Use-case
UpdateProfileData ChangeProfile
Result
ChangeProfile
Request
Service Service
![Page 34: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/34.jpg)
Use-case
Web application
Http
Response
Http
Request
Use-case Use-case
![Page 35: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/35.jpg)
Core principle:
Types are not classes
![Page 36: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/36.jpg)
X
![Page 37: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/37.jpg)
Types are not classes
Set of
valid inputs
Set of
valid outputs
So what is a type?
![Page 38: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/38.jpg)
Types separate data from behavior
Lists
Lists
List.map
List.collect
List.filter
List.etc
![Page 39: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/39.jpg)
Types can be composed too “algebraic types"
![Page 40: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/40.jpg)
Product types
× = Alice, Jan 12th
Bob, Feb 2nd
Carol, Mar 3rd
Set of people Set of dates
type Birthday = Person * Date
![Page 41: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/41.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
![Page 42: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/42.jpg)
Design principle:
Strive for totality
![Page 43: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/43.jpg)
twelveDividedBy(x)
input x maps to 12/x
…
3
2
1
0
…
Domain (int) Codomain (int)
…
4
6
12
…
Totality
int TwelveDividedBy(int input) { switch (input) { case 3: return 4; case 2: return 6; case 1: return 12; case 0: return ??; } }
![Page 44: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/44.jpg)
twelveDividedBy(x)
input x maps to 12/x
…
3
2
1
0
…
Domain (int) Codomain (int)
…
4
6
12
…
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 45: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/45.jpg)
twelveDividedBy(x)
input x maps to 12/x
…
3
2
1
0
…
Domain (int) Codomain (int)
…
4
6
12
…
Totality
int TwelveDividedBy(int input) { switch (input) { case 3: return 4; case 2: return 6; case 1: return 12; case 0: throw InvalidArgException; } }
int -> int
This type signature is a lie!
You tell me you can handle 0, and then you
complain about it?
![Page 46: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/46.jpg)
twelveDividedBy(x)
input x maps to 12/x
…
3
2
1
-1
…
NonZeroInteger int
…
4
6
12
…
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 doesn’t have to be handled
NonZeroInteger -> int
0 is missing
Types are documentation
![Page 47: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/47.jpg)
twelveDividedBy(x)
input x maps to 12/x
…
3
2
1
0
-1
…
int Option<Int>
…
Some 4
Some 6
Some 12
None
…
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 48: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/48.jpg)
Design principle:
Use static types for domain
modelling and documentation
Static types only! Sorry Clojure and JS
developers
![Page 49: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/49.jpg)
Big topic! Not enough time today !
More on DDD and designing with types at
fsharpforfunandprofit.com/ddd
![Page 50: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/50.jpg)
FUNCTIONS AS
PARAMETERS
![Page 51: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/51.jpg)
Guideline:
Parameterize all the things
![Page 52: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/52.jpg)
Parameterize all the things
let printList() = for i in [1..10] do printfn "the number is %i" i
![Page 53: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/53.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 54: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/54.jpg)
Parameterize all the things
let printList anAction aList = for i in aList do anAction i
FPers would parameterize the action as well:
We've decoupled the behavior from the data.
Any list, any action!
![Page 55: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/55.jpg)
Parameterize all the things
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; }
![Page 56: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/56.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
![Page 57: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/57.jpg)
Parameterize all the things
let 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
Lots of collection functions like this: "fold", "map", "reduce", "collect", etc.
![Page 58: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/58.jpg)
Tip:
Function types are "interfaces"
![Page 59: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/59.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 60: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/60.jpg)
Function types are interfaces
interface IBunchOfStuff { int DoSomething(int x); }
An interface with one method is a just a function type
type IBunchOfStuff: int -> int
Any function with that type is compatible with it
let add2 x = x + 2 // int -> int let times3 x = x * 3 // int -> int
![Page 61: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/61.jpg)
Strategy pattern
class MyClass { public MyClass(IBunchOfStuff strategy) {..} int DoSomethingWithStuff(int x) { return _strategy.DoSomething(x) } }
Object-oriented strategy pattern:
![Page 62: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/62.jpg)
Strategy pattern
int int
int->int
int->int
let DoSomethingWithStuff strategy x = strategy x
Functional strategy pattern:
IBunchOfStuff as parameter
![Page 63: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/63.jpg)
Decorator pattern using function parameter:
let isEven x = (x % 2 = 0) // int -> bool
isEven int bool
logger int bool
let logger f input = let output = f input // then log input and output // return output
![Page 64: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/64.jpg)
Decorator pattern using function parameter:
let isEven x = (x % 2 = 0) // int -> bool
isEven int bool
logger int bool
let isEvenWithLogging = logger isEven // int -> bool
Substitutable for original isEven
![Page 65: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/65.jpg)
Decorator pattern using function composition:
let isEven x = (x % 2 = 0) // int -> bool
isEven int bool
isEven int bool log int int log bool bool
let isEvenWithLogging = log >> isEven >> log // int -> bool
Substitutable for original isEven
Composition!
![Page 66: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/66.jpg)
Bad news:
Composition patterns
only work for functions that
have one parameter!
![Page 67: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/67.jpg)
Good news!
Every function is a
one parameter function
![Page 68: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/68.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)
Normal (Two parameters)
![Page 69: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/69.jpg)
let three = 1 + 2
let three = (+) 1 2
let three = ((+) 1) 2
"+" is a two parameter function
![Page 70: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/70.jpg)
let three = 1 + 2
let three = (+) 1 2
let three = ((+) 1) 2
let add1 = (+) 1
let three = add1 2
No, "+" is a one param function!
![Page 71: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/71.jpg)
Pattern:
Partial application
![Page 72: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/72.jpg)
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
Can reuse "hello" in many places now!
![Page 73: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/73.jpg)
Pattern:
Use partial application when
working with lists
![Page 74: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/74.jpg)
let names = ["Alice"; "Bob"; "Scott"] names |> List.iter hello
let hello = printfn "Hello, my name is %s"
let add1 = (+) 1 let equals2 = (=) 2
[1..100] |> List.map add1 |> List.filter equals2
![Page 75: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/75.jpg)
Pattern:
Use partial application to do
dependency injection
![Page 76: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/76.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 ignorant
This function requires a connection
The partially applied function does NOT require a connection – it's baked in!
![Page 77: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/77.jpg)
type GetCustomer = CustomerId -> Customer
let getCustomerFromMemory dict (customerId:CustomerId) = dict.Get(customerId)
type of getCustomerFromMemory = Dictionary<Id,Customer> -> CustomerId -> Customer
let getCustomer2 = getCustomerFromMemory dict // getCustomer2 : CustomerId -> Customer
This function requires a dictionary
The partially applied function does NOT require a dictionary – it's baked in!
![Page 78: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/78.jpg)
Pattern:
The Hollywood principle*:
continuations
![Page 79: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/79.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
(who put it in charge?)
![Page 80: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/80.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 81: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/81.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!
Wouldn't it be nice if we could somehow "bake in" the two behaviour functions....
![Page 82: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/82.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 //test let good1 = divide1 6 3 let 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 83: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/83.jpg)
let divide ifZero ifSuccess top bottom = if (bottom=0) then ifZero() else ifSuccess (top/bottom)
Continuations
let ifZero2() = None let ifSuccess2 x = Some x let divide2 = divide ifZero2 ifSuccess2 //test let good2 = divide2 6 3 let 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 84: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/84.jpg)
let divide ifZero ifSuccess top bottom = if (bottom=0) then ifZero() else ifSuccess (top/bottom)
Continuations
let ifZero3() = failwith "div by 0" let ifSuccess3 x = x let divide3 = divide ifZero3 ifSuccess3 //test let good3 = divide3 6 3 let 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 85: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/85.jpg)
Pattern:
Chaining callbacks with
continuations
![Page 86: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/86.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
I know you could do early returns, but bear with me...
![Page 87: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/87.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
![Page 88: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/88.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 89: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/89.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! But there is a pattern we can exploit...
![Page 90: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/90.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 // do something with z.Value // in this block else None else None else None
![Page 91: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/91.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 // do something with y.Value // in this block else None else None
![Page 92: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/92.jpg)
Pyramid of doom: option example
let example input = let x = doSomething input if x.IsSome then // do something with x.Value // in this block else None
Can you see the pattern?
![Page 93: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/93.jpg)
if opt.IsSome then //do something with opt.Value else None
![Page 94: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/94.jpg)
let ifSomeDo f opt = if opt.IsSome then f opt.Value else None
![Page 95: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/95.jpg)
let example input = doSomething input |> ifSomeDo doSomethingElse |> ifSomeDo doAThirdThing |> ifSomeDo (fun z -> Some z)
let ifSomeDo f opt = if opt.IsSome then f opt.Value else None
![Page 96: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/96.jpg)
MONADS
![Page 97: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/97.jpg)
A switch analogy
Some
None
Input ->
![Page 98: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/98.jpg)
Connecting switches
on Some
Bypass on None
![Page 99: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/99.jpg)
Connecting switches
![Page 100: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/100.jpg)
Connecting switches
![Page 101: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/101.jpg)
Composing switches
>> >>
Composing one-track functions is fine...
![Page 102: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/102.jpg)
Composing switches
>> >>
... and composing two-track functions is fine...
![Page 103: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/103.jpg)
Composing switches
... but composing switches is not allowed!
![Page 104: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/104.jpg)
How to combine the
mismatched functions?
![Page 105: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/105.jpg)
“Bind” is the answer!
Bind all the things!
![Page 106: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/106.jpg)
Composing switches
Two-track input Two-track input
One-track input Two-track input
![Page 107: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/107.jpg)
Building an adapter block
Two-track input
Slot for switch function
Two-track output
![Page 108: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/108.jpg)
Building an adapter block
Two-track input Two-track output
![Page 109: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/109.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 110: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/110.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 111: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/111.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 112: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/112.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 113: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/113.jpg)
Pattern:
Use bind to chain options
![Page 114: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/114.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 115: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/115.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 116: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/116.jpg)
Pattern:
Use bind to chain tasks
![Page 117: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/117.jpg)
Connecting tasks
When task completes Wait Wait
![Page 118: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/118.jpg)
Pyramid of doom: using bind for tasks
let taskBind f task = task.WhenFinished (fun taskResult -> f taskResult)
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
a.k.a “promise” “future”
![Page 119: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/119.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)
This pattern is also a “monadic bind”
![Page 120: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/120.jpg)
Pattern:
Use bind to chain error handlers
![Page 121: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/121.jpg)
Use case without error handling
string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); validateRequest(request); canonicalizeEmail(request); db.updateDbFromRequest(request); smtpServer.sendEmail(request.Email) return "OK"; }
![Page 122: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/122.jpg)
Use case with error handling
string 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 123: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/123.jpg)
Use case with error handling
string 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 124: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/124.jpg)
Use case with error handling
string 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 125: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/125.jpg)
Use case with error handling
string 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 126: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/126.jpg)
Use case with error handling
string 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 127: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/127.jpg)
A structure for managing errors
Request Success Validate
Failure
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
![Page 128: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/128.jpg)
Connecting switches
Validate UpdateDb SendEmail
![Page 129: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/129.jpg)
Connecting switches
Validate UpdateDb SendEmail
![Page 130: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/130.jpg)
Functional flow without error handling
let updateCustomer = receiveRequest |> validateRequest |> canonicalizeEmail |> updateDbFromRequest |> sendEmail |> returnMessage
Before
One track
![Page 131: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/131.jpg)
let updateCustomerWithErrorHandling =
receiveRequest |> validateRequest |> canonicalizeEmail |> updateDbFromRequest |> sendEmail |> returnMessage
Functional flow with error handling
After
See my talk on Friday or
fsharpforfunandprofit.com/rop
Two track
![Page 132: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/132.jpg)
MAPS
![Page 133: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/133.jpg)
World of normal values
int string bool
World of options
int option string option bool option
![Page 134: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/134.jpg)
World of options
World of normal values
int string bool
int option string option bool option
![Page 135: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/135.jpg)
World of options
World of normal values
int option string option bool option
int string bool
![Page 136: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/136.jpg)
let add42ToOption opt = if opt.IsSome then let newVal = add42 opt.Value Some newVal else None
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
![Page 137: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/137.jpg)
World of options
World of normal values
add42
![Page 138: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/138.jpg)
World of options
World of normal values
add42
![Page 139: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/139.jpg)
Lifting
World of options
World of normal values
T -> U
option<T> -> option<U>
Option.map
![Page 140: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/140.jpg)
World of options
World of normal values
add42
add42ToOption Some 1 |>
1 |> // 43
// Some 43
![Page 141: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/141.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 1 |> add42 // 43
let add42ToOption = Option.map add42 Some 1 |> add42ToOption // Some 43
![Page 142: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/142.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 1 |> add42 // 43
Some 1 |> Option.map add42 // Some 43
![Page 143: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/143.jpg)
Lifting to lists
World of lists
World of normal values
T -> U
List<T> -> List<U>
List.map
![Page 144: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/144.jpg)
The right way to code with wrapped types
[1;2;3] |> List.map add42 // [43;44;45]
![Page 145: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/145.jpg)
Lifting to async
World of async
World of normal values
T -> U
async<T> -> async<U>
Async.map
![Page 146: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/146.jpg)
Guideline:
Most wrapped generic types
have a “map”. Use it!
![Page 147: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/147.jpg)
Guideline:
If you create your own generic type,
create a “map” for it.
![Page 148: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/148.jpg)
MONOIDS
![Page 149: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/149.jpg)
Mathematics
Ahead
![Page 150: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/150.jpg)
Thinking like a mathematician
1 + 2 = 3
1 + (2 + 3) = (1 + 2) + 3
1 + 0 = 1
0 + 1 = 1
![Page 151: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/151.jpg)
1 + 2 = 3
Some things
A way of combining them
![Page 152: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/152.jpg)
2 x 3 = 6
Some things
A way of combining them
![Page 153: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/153.jpg)
"a" + "b" = "ab"
Some things
A way of combining them
![Page 154: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/154.jpg)
concat([a],[b]) = [a; b]
Some things
A way of combining them
![Page 155: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/155.jpg)
1 + 2
1 + 2 + 3
1 + 2 + 3 + 4
Is an integer
Is an integer
A pairwise operation has become an operation that
works on lists!
![Page 156: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/156.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 157: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/157.jpg)
1 - (2 - 3) = (1 - 2) - 3
Order of combining does matter
![Page 158: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/158.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 159: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/159.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 160: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/160.jpg)
"" + "hello" = "hello"
"hello" + "" = "hello"
“Zero” for strings
![Page 161: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/161.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 162: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/162.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 163: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/163.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 164: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/164.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 165: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/165.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 166: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/166.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 167: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/167.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 168: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/168.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 169: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/169.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 170: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/170.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 171: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/171.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
![Page 172: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/172.jpg)
Pattern: Simplifying aggregation code with monoids
![Page 173: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/173.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 addPair line1 line2 =
let newQty = line1.Qty + line2.Qty
let newTotal = line1.Total + line2.Total
{Qty=newQty; Total=newTotal}
orderLines |> List.reduce addPair
// {Qty=6; Total= 25.96}
Any combination of monoids is also a monoid
Write a pairwise combiner
Profit!
![Page 174: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/174.jpg)
Pattern: Convert non-monoids to monoids
![Page 175: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/175.jpg)
Customer
+
Customer
+
Customer
Customer Stats
+
Customer Stats
+
Customer Stats
Reduce
Map
Not a monoid A monoid
Customer Stats
(Total)
![Page 176: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/176.jpg)
Hadoop make me a sandwich
https://twitter.com/daviottenheimer
/status/532661754820829185
![Page 177: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/177.jpg)
Guideline:
Convert expensive monoids
to cheap monoids
![Page 178: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/178.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 179: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/179.jpg)
Pattern: Seeing monoids everywhere
![Page 180: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/180.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 181: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/181.jpg)
Is function composition a monoid?
>> Function 1
apple -> banana Function 2
banana -> cherry
New Function
apple -> cherry
Not the same thing.
Not a monoid
![Page 182: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/182.jpg)
Is function composition a monoid?
>> Function 1
apple -> apple
Same thing
Function 2
apple -> apple
Function 3
apple -> apple
A monoid!
![Page 183: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/183.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 184: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/184.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 185: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/185.jpg)
let plus1 x = x + 1 // int->int
let times2 x = x * 2 // int->int
let subtract42 x = x – 42 // int->int
Reduce
plus1ThenTimes2ThenSubtract42 // int->int
Endomorphisms
Another endomorphism!
![Page 186: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/186.jpg)
Event sourcing
Any function containing an endomorphism can be converted into a monoid!
Event application function:
Is an endomorphism
Event -> State -> State
![Page 187: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/187.jpg)
apply event1 // State -> State
apply event2 // State -> State
apply event3 // State -> State
Reduce
applyAllEventsAtOnce // State -> State
Endomorphism
Another endomorphism!
Partial application of event
• incremental updates
• can handle missing events
![Page 188: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/188.jpg)
Monads vs. monoids?
![Page 189: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/189.jpg)
Series combination
= +
Result is same kind of thing
(Closure)
![Page 190: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/190.jpg)
Series combination
+
Order not important
(Associative) Monoid!
+ ( )
+ + ( )
![Page 191: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/191.jpg)
Parallel combination
= + Same thing (Closure)
Order not important
(Associative)
Monoid!
![Page 192: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/192.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 go to jail
– You lose monoid benefits such as aggregation
![Page 193: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/193.jpg)
A monad is just a monoid in
the category of endofunctors!
![Page 194: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/194.jpg)
A monad is just a monoid in
the category of endofunctors!
![Page 195: Functional Patterns](https://reader031.vdocument.in/reader031/viewer/2022020106/55cf8f36550346703b9a00cd/html5/thumbnails/195.jpg)
THANKS!