(Video and code at http://fsharpforfunandprofit.com/composition)
Composition is a fundamental principle of functional programming, but how is it different from an object-oriented approach, and how do you use it in practice?
In this talk for beginners, we'll start by going over the basic concepts of functional programming, and then look at some different ways that composition can be used to build large things from small things.
After that, we'll see how composition is used in practice, beginning with a simple FizzBuzz example, and ending with a complete (object-free!) web application.
Optimizing AI for immediate response in Smart CCTV
The Power of Composition
1. The Power Of Composition
@ScottWlaschin
fsharpforfunandprofit.com
^ for beginners in FP
2. The Power Of Composition
• The philosophy of composition
• Functional programming principles
– Functions and how to compose them
– Types and how to compose them
• Composition in practice
– Two simple examples
– FizzBuzz gone carbonated
– A web service
4. How to learn functional programming
• Have a "beginner's mind"
• Forget everything you know about object-
oriented programming
– No loops!
– No variables!
– No objects!
5. The "Are you ready to learn FP?" test
• What is a class?
• What is a method?
• What is a for-loop?
• What is inheritance?
You *must* pass
this test before
continuing!
6. Answers!
• What is a class?
– Correct answer: "I don't know"
• What is a method?
– Correct answer: "No idea"
• What is a for-loop?
– Correct answer: "I couldn't tell you"
• What is inheritance?
– Correct answer: "Search me"
10. Lego Philosophy
1. All pieces are designed to be connected
2. Connect two pieces together and get
another "piece" that can still be connected
3. The pieces are reusable in many contexts
16. Wooden RailwayTrack Philosophy
1. All pieces are designed to be connected
2. Connect two pieces together and get
another "piece" that can still be connected
3. The pieces are reusable in many contexts
21. Unix Philosophy
• Write programs that do one thing well.
– To do a new job, build afresh rather than complicate
old programs by adding new "features".
• Write programs to work together.
– Expect the output of every program to become the
input to another, as yet unknown, program.
• Write programs to handle text streams, because
that is a universal interface.
All pieces are designed to be connected
The pieces are reusable
You don't need to create a special adapter to make connections.
34. Function composition
New Function
apple -> cherry
A Very Important Point: For composition to work properly:
• Data must be immutable
• Functions must be self-contained, with no strings attached:
no side-effects, no I/O, no globals, etc
35. let add1 x = x + 1
let double x = x + x
let add1_double = add1 >> double
let x = add1_double 5 // 12
Composition
DoubleAdd1
48. • "monoids"
– for combining strings, lists, etc.
• "monads"
– for composing functions with effects
• CategoryTheory
– or, "Composition Theory"
More kinds of composition
for functional programmers…
50. So, what is a type then?
A type is a just a name
for a set of things
Set of
valid inputs
Set of
valid outputs
Function
51. Set of
valid inputs
Set of
valid outputs
Function
1
2
3
4
5
6
This is type
"integer"
A type is a just a name
for a set of things
52. Set of
valid inputs
Set of
valid outputs
Function
This is type
"string"
"abc"
"but"
"cobol"
"double"
"end"
"float"
A type is a just a name
for a set of things
53. Set of
valid inputs
Set of
valid outputs
Function
This is type
"Person"
Donna Roy
Javier Mendoza
Nathan Logan
Shawna Ingram
Abel Ortiz
Lena Robbins
GordonWood
A type is a just a name
for a set of things
54. Set of
valid inputs
Set of
valid outputs
Function
This is type
"Fruit"
A type is a just a name
for a set of things
55. Set of
valid inputs
Set of
valid outputs
Function
This is a type of
Fruit->Fruit functions
A type is a just a name
for a set of things
58. New types are built from smaller types by:
Composing with “AND”
Composing with “OR”
59. Example: pairs, tuples, records
FruitSalad = One each of and and
Compose with “AND”
type FruitSalad = {
Apple: AppleVariety
Banana: BananaVariety
Cherry: CherryVariety
}
60. Snack = or or
Compose with “OR”
type Snack =
| Apple of AppleVariety
| Banana of BananaVariety
| Cherry of CherryVariety
62. Example of some requirements:
We accept three forms of payment:
Cash, Check, or Card.
For Cash we don't need any extra information
For Checks we need a check number
For Cards we need a card type and card number
63. interface IPaymentMethod
{..}
class Cash() : IPaymentMethod
{..}
class Check(int checkNo): IPaymentMethod
{..}
class Card(string cardType, string cardNo) : IPaymentMethod
{..}
In OO design you would probably implement it as an
interface and a set of subclasses, like this:
64. type CheckNumber = int
type CardNumber = string
In F# you would probably implement by composing
types, like this:
65. type CheckNumber = ...
type CardNumber = …
type CardType = Visa | Mastercard
type CreditCardInfo = {
CardType : CardType
CardNumber : CardNumber
}
66. type CheckNumber = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| Check of CheckNumber
| Card of CreditCardInfo
67. type CheckNumber = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| Check of CheckNumber
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD
68. type CheckNumber = ...
type CardNumber = ...
type CardType = ...
type CreditCardInfo = ...
type PaymentMethod =
| Cash
| Check of CheckNumber
| Card of CreditCardInfo
type PaymentAmount = decimal
type Currency = EUR | USD
type Payment = {
Amount : PaymentAmount
Currency: Currency
Method: PaymentMethod }
70. type Deal = Deck –› (Deck * Card)
type PickupCard = (Hand * Card) –› Hand
Types are executable documentation
type Suit = Club | Diamond | Spade | Heart
type Rank = Two | Three | Four | Five | Six | Seven | Eight
| Nine |Ten | Jack | Queen | King | Ace
type Card = Suit * Rank
type Hand = Card list
type Deck = Card list
type Player = {Name:string; Hand:Hand}
type Game = {Deck:Deck; Players: Player list}
The domain on one screen!
71. Types are executable documentation
type CardType =Visa | Mastercard
type CardNumber = CardNumber of string
type CheckNumber = CheckNumber of int
type PaymentMethod =
| Cash
| Check of CheckNumber
| Card of CardType * CardNumber
72. A big topic and not enough time
More on DDD and designing with types at
fsharpforfunandprofit.com/ddd
74. Think of a number
• Think of a number.
• Add one to it.
• Square it.
• Subtract one.
• Divide by the number you first thought of.
• Subtract the number you first thought of.
• The answer is TWO!
75. Think of a number
AddOne
SquareItSubtractOne
DivideByTheNumberYouThoughtOf
SubtractTheNumberYouThoughtOf
TheNumberYouThoughtOf
Answer
76. let thinkOfANumber numberYouThoughtOf =
// define a function for each step
let addOne x = x + 1
let squareIt x = x * x
let subtractOne x = x - 1
let divideByTheNumberYouThoughtOf x = x / numberYouThoughtOf
let subtractTheNumberYouThoughtOf x = x - numberYouThoughtOf
// then combine them using piping
numberYouThoughtOf
|> addOne
|> squareIt
|> subtractOne
|> divideByTheNumberYouThoughtOf
|> subtractTheNumberYouThoughtOf
86. To Roman Numerals
• Task: convert an integer to Roman Numerals
• V = 5, X = 10, C = 100 etc
87. To Roman Numerals
• Use the "tally" approach
– Start with N copies of "I"
– Replace five "I"s with a "V"
– Replace two "V"s with a "X"
– Replace five "X"s with a "L"
– Replace two "L"s with a "C"
– Replace five "C"s with a "D"
– Replace two "D"s with a "M"
93. Input A
Uncurried
Function
Input B
Output C
Curried
Function
Input A
Intermediate
Function
Output CInput B
What is currying?
after currying
Currying means that *every* function can be converted
to a series of one input functions
96. Partial Application
Replace ReplaceOldNew
Old New
Old
New
let replace_IIIII_V = replace "IIIII" "V"
// replace_IIIII_V is a function
let replace_VV_X = replace "VV" "X"
// replace_VV_X is a function
Only 2 parameters
passed in
98. let toRomanNumerals number =
let replace_IIIII_V = replace "IIIII" "V"
let replace_VV_X = replace "VV" "X"
let replace_XXXXX_L = replace "XXXXX" "L"
let replace_LL_C = replace "LL" "C"
let replace_CCCCC_D = replace "CCCCC" "D"
let replace_DD_M = replace "DD" "M"
String.replicate number "I"
|> replace_IIIII_V
|> replace_VV_X
|> replace_XXXXX_L
|> replace_LL_C
|> replace_CCCCC_D
|> replace_DD_M
99. Inline Partial Application
let add x y = x + y
let multiply x y = x * y
5
|> add 2
|> multiply 2
Piping
"inline" partial application
100. Inline Partial Application
let add x y = x + y
let multiply x y = x * y
[1..10]
|> List.map (add 2)
|> List.map (multiply 2)
Two "inline" partial applications
101. let toRomanNumerals number =
String.replicate number "I"
|> replace "IIIII" "V"
|> replace "VV" "X"
|> replace "XXXXX" "L"
|> replace "LL" "C"
|> replace "CCCCC" "D"
|> replace "DD" "M"
With inline partial application
102. let toRomanNumerals number =
String.replicate number "I"
|> replace "IIIII" "V"
|> replace "VV" "X"
|> replace "XXXXX" "L"
|> replace "LL" "C"
|> replace "CCCCC" "D"
|> replace "DD" "M"
With inline partial application
// can easily add new segments to the pipeline
|> replace "VIIII" "IX"
|> replace "IIII" "IV"
|> replace "LXXXX" "XC"
105. FizzBuzz definition
• Write a program that prints the numbers
from 1 to 100
• But:
– For multiples of three print "Fizz" instead
– For multiples of five print "Buzz" instead
– For multiples of both three and five print
"FizzBuzz" instead.
106. let fizzBuzz max =
for n in [1..max] do
if (isDivisibleBy n 15) then
printfn "FizzBuzz"
else if (isDivisibleBy n 3) then
printfn "Fizz"
else if (isDivisibleBy n 5) then
printfn "Buzz"
else
printfn "%i" n
let isDivisibleBy n divisor = (n % divisor) = 0
A simple implementation
110. type CarbonationResult =
| Uncarbonated of int // unprocessed
| Carbonated of string // "Fizz", Buzz", etc
FizzBuzz core
let carbonate divisor label n =
if (isDivisibleBy n divisor) then
Carbonated label
else
Uncarbonated n
Idea from http://weblog.raganwald.com/2007/01/dont-overthink-fizzbuzz.html
116. >> >>
... and composing two-track functions is fine...
117.
... but composing points/switches is not allowed!
118. let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
let result3 = n |> carbonate 3 "Fizz"
match result3 with
| Carbonated str ->
str
| Uncarbonated n ->
let result5 = n |> carbonate 5 "Buzz"
match result5 with
| Carbonated str ->
str
| Uncarbonated n ->
string n // convert to string
First implementation attempt
119. let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
let result3 = n |> carbonate 3 "Fizz"
match result3 with
| Carbonated str ->
str
| Uncarbonated n ->
let result5 = n |> carbonate 5 "Buzz"
match result5 with
| Carbonated str ->
str
| Uncarbonated n ->
// do something with Uncarbonated value
120. let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
let result3 = n |> carbonate 3 "Fizz"
match result3 with
| Carbonated str ->
str
| Uncarbonated n ->
// do something with Uncarbonated value
121. let fizzbuzz n =
let result15 = n |> carbonate 15 "FizzBuzz"
match result15 with
| Carbonated str ->
str
| Uncarbonated n ->
// do something with Uncarbonated value
123. let ifUncarbonatedDo f result =
match result with
| Carbonated str ->
Carbonated str
| Uncarbonated n ->
f n
124. let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> carbonateRemaining
let carbonateRemaining result =
match result with
| Carbonated str ->
str
| Uncarbonated n ->
string(n)
127. Solution: the "map" transformer
List input List outputFizzBizz for lists
FizzBizz for one number
List.map
128. // final version of FizzBuzz
let fizzbuzz100 =
[1..100]
|> List.map fizzBuzz
|> List.iter (printfn "%s") // I/O only at end
let fizzbuzz n =
n
|> carbonate 15 "FizzBuzz"
|> ifUncarbonatedDo (carbonate 3 "Fizz")
|> ifUncarbonatedDo (carbonate 5 "Buzz")
|> carbonateRemaining
135. let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
136. let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
137. let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
138. let bind nextFunction result =
match result with
| Uncarbonated n ->
nextFunction n
| Carbonated str ->
Carbonated str
Two-track input Two-track output
139. FP terminology
• A monad is
– A data type
– With an associated "bind" function
– (and some other stuff)
• A monadic function is
– A switch/points function
– "bind" is used to compose them
142. 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 ->
etc
143. let bind f task =
task.WhenFinished (fun taskResult ->
f taskResult)
let taskExample input =
startTask input
|> bind startAnotherTask
|> bind startThirdTask
|> bind ...
Rewritten using bind:
144. let ( >>= ) x f = x |> bind
let taskExample input =
startTask input
>>= startAnotherTask
>>= startThirdTask
>>= ...
Rewritten using >>=
A common symbol for "bind"
146. Example of scenario with errors
Name is blank
Email not valid
Validate request
Canonicalize email
Fetch existing user record
Update existing user record
User not found
Db error
Authorization error
Timeout
150. Validate Canonicalize DbFetch DbUpdate
Generates a possible error Generates a possible errorAlways succeeds Doesn't return
How can we glue these
mismatched functions together?
164. Review
• The philosophy of composition
– Connectable, no adapters, reusable parts
• FP principles:
– Composable functions
– Composable types
• Basic composition
– Composition with ">>"
– Piping with "|>"
• Using partial application to help composition
• Monads and monadic functions
– Composition using "bind" / ">>="
– Kleisli composition using ">=>"
165. Slides and video here
fsharpforfunandprofit.com/composition
Thank you!
"Domain modelling Made Functional" book
fsharpforfunandprofit.com/books
@ScottWlaschin Me on twitter
fsharpforfunandprofit.com/videos