Successfully reported this slideshow.
Upcoming SlideShare
×

# The Power of Composition

2,901 views

Published on

(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.

Published in: Software
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

### The Power of Composition

1. 1. The Power Of Composition @ScottWlaschin fsharpforfunandprofit.com ^ for beginners in FP
2. 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
3. 3. PREREQUISITES
4. 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. 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. 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"
7. 7. THE PHILOSOPHY OF COMPOSITION
8. 8. Prerequisites for understanding composition • You must have been a child at some point • You must have played with Lego • You must have played with toy trains
9. 9. Lego Philosophy
10. 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
11. 11. All pieces are designed to be connected
12. 12. Connect two pieces together and get another "piece" that can still be connected
13. 13. The pieces are reusable in different contexts
14. 14. Make big things from small things in the same way
15. 15. 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
16. 16. All pieces are designed to be connected
17. 17. Connect two pieces together and get another "piece" that can still be connected You can keep adding and adding.
18. 18. The pieces are reusable in different contexts
19. 19. Make big things from small things in the same way
20. 20. 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.
21. 21. THE PRINCIPLES OF FUNCTIONAL PROGRAMMING
22. 22. Core principles of FP Function Types are not classes Functions are things Composition everywhere
23. 23. Core FP principle: Functions are things Function
24. 24. Functions as things The Tunnel of Transformation Function apple -> banana A function is a thing which transforms inputs to outputs
25. 25. A function is a standalone thing, not attached to a class It can be used for inputs and outputs of other functions
26. 26. input A function can be an output A function is a standalone thing
27. 27. output A function can be an input A function is a standalone thing
28. 28. input output A function can be a parameter A function is a standalone thing
29. 29. Core FP principle: Composition everywhere
30. 30. Function composition Function 1 apple -> banana Function 2 banana -> cherry
31. 31. Function composition >> Function 1 apple -> banana Function 2 banana -> cherry
32. 32. Function composition New Function apple -> cherry Can't tell it was built from smaller functions! Where did the banana go? (abstraction)
33. 33. 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
34. 34. let add1 x = x + 1 let double x = x + x let add1_double = add1 >> double let x = add1_double 5 // 12 Composition DoubleAdd1
36. 36. Piping
37. 37. add1 5 // = 6 double (add1 5) // = 12 square (double (add1 5)) // = 144
38. 38. 5 |> add1 // = 6 5 |> add1 |> double // = 12 5 |> add1 |> double |> square // = 144 add1 double square5 6 12 144
39. 39. Building big things from functions It's compositions all the way up
40. 40. Low-level operation ToUpper stringstring
41. 41. 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
42. 42. Service Use-case UpdateProfileData ChangeProfile Result ChangeProfile Request Service Service
43. 43. Use-case Web application Http Response Http Request Use-case Use-case
44. 44. Http Response Http Request
45. 45. • "monoids" – for combining strings, lists, etc. • "monads" – for composing functions with effects • CategoryTheory – or, "Composition Theory" More kinds of composition for functional programmers…
46. 46. Core FP principle: Types are not classes
47. 47. 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
48. 48. 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
49. 49. 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
50. 50. 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
51. 51. 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
52. 52. 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
53. 53. Composition everywhere: Types can be composed too
54. 54. Algebraic type system
55. 55. New types are built from smaller types by: Composing with “AND” Composing with “OR”
56. 56. Example: pairs, tuples, records FruitSalad = One each of and and Compose with “AND” type FruitSalad = { Apple: AppleVariety Banana: BananaVariety Cherry: CherryVariety }
57. 57. Snack = or or Compose with “OR” type Snack = | Apple of AppleVariety | Banana of BananaVariety | Cherry of CherryVariety
58. 58. Real world example of type composition
59. 59. 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
60. 60. 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:
61. 61. type CheckNumber = int type CardNumber = string In F# you would probably implement by composing types, like this:
62. 62. type CheckNumber = ... type CardNumber = … type CardType = Visa | Mastercard type CreditCardInfo = { CardType : CardType CardNumber : CardNumber }
63. 63. type CheckNumber = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | Check of CheckNumber | Card of CreditCardInfo
64. 64. type CheckNumber = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | Check of CheckNumber | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD
65. 65. 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 }
66. 66. FP design principle: Types are executable documentation
67. 67. 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!
68. 68. 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
69. 69. A big topic and not enough time   More on DDD and designing with types at fsharpforfunandprofit.com/ddd
70. 70. BASIC COMPOSITION: THINK OF A NUMBER
71. 71. 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!
72. 72. Think of a number AddOne SquareItSubtractOne DivideByTheNumberYouThoughtOf SubtractTheNumberYouThoughtOf TheNumberYouThoughtOf Answer
73. 73. 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
74. 74. IT'S NOT ALWAYSTHIS EASY…
75. 75. function A function BCompose
76. 76. function A function B
77. 77. function A and B Easy!
78. 78. ... But here is a challenge
79. 79. function AInput Output function BInput Output 1 Output 2 function C Input 1 Output Input 2
80. 80. function AInput Output function BInput Output 1 Output 2 Challenge: How can we compose these?
81. 81. function AInput Output function C Input 1 Output Input 2 Challenge: How can we compose these?
82. 82. COMPOSING MULTIPLE INPUTS: ROMAN NUMERALS
83. 83. To Roman Numerals • Task: convert an integer to Roman Numerals • V = 5, X = 10, C = 100 etc
84. 84. 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"
85. 85. The Replace function oldValue outputString newValue inputString Replace
86. 86. Uh-oh! Composition problem Replace I / V Replace V / X Replace X / L 
87. 87. Bad news: Composition patterns only work for functions that have one parameter! 
88. 88. Good news! Every function can be turned into a one parameter function 
89. 89. Haskell Curry We named this technique after him
90. 90. 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
91. 91. Replace Before currying let replace oldValue newValue inputStr = inputStr.Replace(oldValue,newValue)
92. 92. Replace Old New Old New After currying let replace oldValue newValue = fun inputStr -> inputStr.Replace(oldValue,newValue)
93. 93. 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
94. 94. To Roman Numerals integer etc Replicate "I" Replace_IIIII_V Replace_VV_X Replace_XXXXX_L
95. 95. 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
96. 96. Inline Partial Application let add x y = x + y let multiply x y = x * y 5 |> add 2 |> multiply 2 Piping "inline" partial application
97. 97. 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
98. 98. 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
99. 99. 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"
100. 100. functionInput Output function Input 1 Output Input 2 Challenge: How can we compose these?
101. 101. COMPOSING MULTIPLE OUTPUTS: FIZZBUZZ
102. 102. 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.
103. 103. 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
104. 104. Pipeline implementation Handle 3 case Handle 5 case number Answer Handle 15 case Handle remaining
105. 105. number Handle case Carbonated (e.g. "Fizz", "Buzz") Uncarbonated (e.g. 2, 7, 13)
106. 106. Uncarbonated Carbonated Input ->
107. 107. 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
108. 108. 12 |> carbonate 3 "Fizz" // Carbonated "Fizz" 10 |> carbonate 3 "Fizz" // Uncarbonated 10 10 |> carbonate 5 "Buzz" // Carbonated "Buzz"
109. 109. If Uncarbonated If Carbonated bypass How to we compose them?
110. 110. How do we compose these?
111. 111. >> >> Composing one-track functions is fine...
112. 112. >> >> ... and composing two-track functions is fine...
113. 113.   ... but composing points/switches is not allowed!
114. 114. 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
115. 115. 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
116. 116. 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
117. 117. let fizzbuzz n = let result15 = n |> carbonate 15 "FizzBuzz" match result15 with | Carbonated str -> str | Uncarbonated n -> // do something with Uncarbonated value
118. 118. if Carbonated // return the string if Uncarbonated then // do something with the number
119. 119. let ifUncarbonatedDo f result = match result with | Carbonated str -> Carbonated str | Uncarbonated n -> f n
120. 120. 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)
121. 121. MAKING LOOPS COMPOSABLE
122. 122. Another composition problem List of numbers FizzBizz for one number 
123. 123. Solution: the "map" transformer List input List outputFizzBizz for lists FizzBizz for one number List.map
124. 124. // 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
125. 125. THE M-WORD
126. 126. Is there a general solution to handling functions like this?
127. 127. Yes! “Bind” is the answer! Bind all the things!
128. 128. Two-track input Two-track output One-track input Two-track output  
129. 129. Two-track input Two-track output
130. 130. Two-track input Two-track output
131. 131. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
132. 132. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
133. 133. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
134. 134. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
135. 135. 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
136. 136. Example: Using bind to chain tasks
137. 137. When task completesWait Wait a.k.a "promise", "future"
140. 140. let ( >>= ) x f = x |> bind let taskExample input = startTask input >>= startAnotherTask >>= startThirdTask >>= ... Rewritten using >>= A common symbol for "bind"
141. 141. Example: Composing error-generating functions
142. 142. 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
143. 143. Validate Generates a possible error
144. 144. Validate Canonicalize Generates a possible error Always succeeds
145. 145. Validate Canonicalize DbFetch Generates a possible error Generates a possible errorAlways succeeds
146. 146. Validate Canonicalize DbFetch DbUpdate Generates a possible error Generates a possible errorAlways succeeds Doesn't return How can we glue these mismatched functions together?
147. 147. map Converting everything to two-track
148. 148. bind map Converting everything to two-track
149. 149. bind map tee map Converting everything to two-track
150. 150. Validate Canonicalize DbFetch DbUpdate Now we *can* compose them easily! map bind tee, then map
151. 151. KLEISLI COMPOSITION: WEB SERVICE
152. 152. =+ Result is same kind of thing Kleisli Composition
153. 153. Async<HttpContext option>HttpContext A "WebPart" (suave.io library) See suave.io site for more
154. 154. =>=> Result is another WebPart so you can repeat WebPart Composition Kleisli composition symbol
155. 155. path "/hello" >=> OK "Hello" // a WebPart Checks request path (might fail) Sets response
156. 156. choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] Picks first WebPart that succeeds
157. 157. GET >=> choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] Only succeeds if request is a GET
158. 158. let app = choose [ GET >=> choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] POST >=> choose [ path "/hello" >=> OK "Hello POST" path "/goodbye" >=> OK "Goodbye POST" ] ] startWebServer defaultConfig app A complete web app
159. 159. Http Response Http Request As I promised – no classes, no loops, etc!
160. 160. 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 ">=>"
161. 161. Slides and video here fsharpforfunandprofit.com/composition Thank you! "Domain modelling Made Functional" book fsharpforfunandprofit.com/books @ScottWlaschin Me on twitter fsharpforfunandprofit.com/videos