Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

The Power Of Composition (DotNext 2019)

1,200 views

Published on

(Video available at https://fsharpforfunandprofit.com/composition/)

Composition is a fundamental building block 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 some basic examples, and ending with a complete (object-free!) web application.

Published in: Software
  • Making a living taking surveys at home! I have been a stay at home mom for almost 5 years and I am so excited to be able to still stay home, take care of my children and make a living taking surveys on my own computer! It's so easy to get started and I plan to make enough money each week so that my husband can actuallly quit his second job!!! Thank you so much! ♣♣♣ http://t.cn/AieX2Loq
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

The Power Of Composition (DotNext 2019)

  1. 1. DotNext 2019 The Power Of Composition @ScottWlaschin fsharpforfunandprofit.com
  2. 2. The Power Of Composition 1. The philosophy of composition 2. Ideas of functional programming – Functions and how to compose them – Types and how to compose them 3. Composition in practice – Roman Numerals – FizzBuzz gone carbonated – Uh oh, monads! – A web service
  3. 3. THE PHILOSOPHY OF COMPOSITION
  4. 4. 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
  5. 5. Lego Philosophy
  6. 6. Lego Philosophy 1. All pieces are designed to be connected 2. The pieces are reusable in many contexts 3. Connect two pieces together and get another "piece" that can still be connected
  7. 7. All pieces are designed to be connected
  8. 8. The pieces are reusable in different contexts
  9. 9. Connect two pieces together and get another "piece" that can still be connected
  10. 10. Make big things from small things in the same way
  11. 11. Wooden Railway Track Philosophy 1. All pieces are designed to be connected 2. The pieces are reusable in many contexts 3. Connect two pieces together and get another "piece" that can still be connected
  12. 12. All pieces are designed to be connected
  13. 13. The pieces are reusable in different contexts
  14. 14. Connect two pieces together and get another "piece" that can still be connected You can keep adding and adding.
  15. 15. Make big things from small things in the same way
  16. 16. If you understand Lego and wooden railways, then you know everything about composition!
  17. 17. THE IDEAS OF FUNCTIONAL PROGRAMMING
  18. 18. Four ideas behind FP Function 3.Types are not classes 1. Functions are things 2. Build bigger functions using composition 4. Build bigger types using composition
  19. 19. FP idea #1: Functions are things Function
  20. 20. The Tunnel of Transformation Function apple -> banana A function is a thing which transforms inputs to outputs
  21. 21. A function is a standalone thing, not attached to a class
  22. 22. A function is a standalone thing, not attached to a class It can be used for inputs and outputs of other functions
  23. 23. A function can be an output thing input
  24. 24. output A function can be an input thing
  25. 25. input output A function can be a parameter
  26. 26. input output input output
  27. 27. FP idea #2: Build bigger functions using composition
  28. 28. Function 1 apple -> banana Function 2 banana -> cherry
  29. 29. >> Function 1 apple -> banana Function 2 banana -> cherry
  30. 30. New Function apple -> cherry Can't tell it was built from smaller functions! Where did the banana go?
  31. 31. Function composition in F# and C# using the "piping" approach
  32. 32. int add1(int x) => x + 1; int times2(int x) => x * 2; int square(int x) => x * x; add1(5); // = 6 times2(add1(5)); // = 12 square(times2(add1(5))); // = 144 Nested function calls can be confusing if too deep
  33. 33. add15 6 times2 12 square 144
  34. 34. 5 |> add1 // = 6 5 |> add1 |> times2 // = 12 5 |> add1 |> times2 |> square // = 144 add1 times2 square5 6 12 144 F# example
  35. 35. add1 times2 square5 6 12 144 5.Pipe(add1); 5.Pipe(add1).Pipe(times2); 5.Pipe(add1).Pipe(times2).Pipe(square); C# example
  36. 36. Building big things from functions It's compositions all the way up
  37. 37. Low-level operation ToUpper stringstring
  38. 38. 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
  39. 39. Service Use-case UpdateProfileData ChangeProfile Result ChangeProfile Request Service Service
  40. 40. Use-case Web application Http Response Http Request Use-case Use-case
  41. 41. Http Response Http Request Even for complex applications, data flows only in one direction
  42. 42. FP idea #3: Types are not classes
  43. 43. 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
  44. 44. 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
  45. 45. 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
  46. 46. 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
  47. 47. 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
  48. 48. 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
  49. 49. FP idea #4: Types can be composed too
  50. 50. Algebraic type system
  51. 51. Bigger types are built from smaller types by: Composing with “AND” Composing with “OR”
  52. 52. FruitSalad = AND AND Compose with “AND”
  53. 53. Compose with “AND” enum AppleVariety { Red, Green } enum BananaVariety { Yellow, Brown } enum CherryVariety { Tart, Sweet } struct FruitSalad { AppleVariety Apple; BananaVariety Banana; CherryVariety Cherry; } C# example Apple AND Banana AND Cherry
  54. 54. Compose with “AND” type AppleVariety = Red | Green type BananaVariety = Yellow | Brown type CherryVariety = Tart | Sweet type FruitSalad = { Apple: AppleVariety Banana: BananaVariety Cherry: CherryVariety } F# example
  55. 55. Snack = OR OR Compose with “OR” type Snack = | Apple of AppleVariety | Banana of BananaVariety | Cherry of CherryVariety
  56. 56. A real world example of composing types
  57. 57. Some requirements: We accept three forms of payment: Cash, Paypal, or CreditCard. For Cash we don't need any extra information For Paypal we need an email address For Cards we need a card type and card number
  58. 58. interface IPaymentMethod {..} class Cash() : IPaymentMethod {..} class Paypal(string emailAddress): 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:
  59. 59. type EmailAddress = string type CardNumber = string In F# you would probably implement by composing types, like this:
  60. 60. type EmailAddress = ... type CardNumber = … type CardType = Visa | Mastercard type CreditCardInfo = { CardType : CardType CardNumber : CardNumber }
  61. 61. type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo
  62. 62. type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD | RUB
  63. 63. type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD | RUB type Payment = { Amount : PaymentAmount Currency : Currency Method : PaymentMethod }
  64. 64. type EmailAddress = ... type CardNumber = ... type CardType = ... type CreditCardInfo = ... type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo type PaymentAmount = decimal type Currency = EUR | USD | RUB type Payment = { Amount : PaymentAmount Currency : Currency Method : PaymentMethod }
  65. 65. Composable types can be used as executable documentation
  66. 66. type Deal = Deck -> (Deck * Card) type PickupCard = (Hand * Card) -> Hand type Suit = Club | Diamond | Spade | Heart type Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace type Card = { Suit:Suit; Rank: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!
  67. 67. type CardType = Visa | Mastercard type CardNumber = string type EmailAddress = string type PaymentMethod = | Cash | PayPal of EmailAddress | Card of CreditCardInfo | Bitcoin of BitcoinAddress
  68. 68. A big topic and not enough time   More on DDD and designing with types at fsharpforfunandprofit.com/ddd
  69. 69. Composition in practice: Time for some real examples!
  70. 70. COMPOSITION WITH PIPING (ROMAN NUMERALS) Technique #1
  71. 71. To Roman Numerals • Task: How to convert an arabic integer to roman numerals? • 5 => "V" • 12 => "XII" • 107 => "CVII"
  72. 72. To Roman Numerals
  73. 73. 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" – etc
  74. 74. To Roman Numerals number etc Replicate "I" Replace_IIIII_V Replace_VV_X Replace_XXXXX_L
  75. 75. string ToRomanNumerals(int number) { // define a helper function for each step string replace_IIIII_V(string s) => s.Replace("IIIII", "V"); string replace_VV_X(string s) => s.Replace("VV", "X"); string replace_XXXXX_L(string s) => s.Replace("XXXXX", "L"); string replace_LL_C(string s) => s.Replace("LL", "C"); // then combine them using piping return new string('I', number) .Pipe(replace_IIIII_V) .Pipe(replace_VV_X) .Pipe(replace_XXXXX_L) .Pipe(replace_LL_C); } C# example
  76. 76. let toRomanNumerals number = // define a helper function for each step let replace_IIIII_V str = replace "IIIII" "V" str let replace_VV_X str = replace "VV" "X" str let replace_XXXXX_L str = replace "XXXXX" "L" str let replace_LL_C str = replace "LL" "C" str // then combine them using piping String.replicate number "I" |> replace_IIIII_V |> replace_VV_X |> replace_XXXXX_L |> replace_LL_C F# example
  77. 77. IT'S NOT ALWAYSTHIS EASY…
  78. 78. function A function BCompose
  79. 79. function A function B
  80. 80. function A and B Easy!
  81. 81. ... But here is a challenge
  82. 82. function AInput Output function B Input 1 Output Input 2
  83. 83. function AInput Output function B Input 1 Output Input 2 Challenge #1: How can we compose these? 
  84. 84. COMPOSITIONWITH CURRYING (ROMAN NUMERALS) Technique #2
  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 Function as output
  91. 91. Input A Uncurried Function Input B Output C Curried Function Input A Intermediate Function Output CInput B What is currying? One input One input Currying means that *every* function can be converted to a series of one input functions
  92. 92. Replace Before currying input.Replace(oldValue, newValue); string output
  93. 93. Replace Old New Old New After currying Func<string,string> replace(string oldVal, string newVal) => input => input.Replace(oldVal, newVal);
  94. 94. Replace Old New Old New After currying Func<string,string> replace(string oldVal, string newVal) => input => input.Replace(oldVal, newVal);
  95. 95. Func<string,string> replace(string oldVal, string newVal) => input => input.Replace(oldVal, newVal); Replace Old New Old New After currying This lambda (function) is returned one-parameter function
  96. 96. string ToRomanNumerals(int number) { // define a general helper function Func<string,string> replace( string oldValue, string newValue) => input => input.Replace(oldValue, newValue); // then use piping return new string('I', number) .Pipe(replace("IIIII","V")) .Pipe(replace("VV","X")) .Pipe(replace("XXXXX","L")) .Pipe(replace("LL","C")); } C# example
  97. 97. let toRomanNumerals number = // no helper function needed. // currying occurs automatically in F# // combine using piping String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" F# example
  98. 98. let toRomanNumerals number = // no helper function needed. // currying occurs automatically in F# // combine using piping String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" Only 2 of the 3 parameters are passed ?
  99. 99. Partial Application
  100. 100. Partial Application let add x y = x + y let multiply x y = x * y 5 |> add 2 |> multiply 3 Piping provides the missing argument partial application
  101. 101. Partial Application Replace ReplaceOldNew Old New Old New String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" Only 2 parameters passed in Piping provides the missing argument
  102. 102. Pipelines are extensible
  103. 103. let toRomanNumerals number = String.replicate number "I" |> replace "IIIII" "V" |> replace "VV" "X" |> replace "XXXXX" "L" |> replace "LL" "C" Composable => extensible // can easily add new segments to the pipeline |> replace "VIIII" "IX" |> replace "IIII" "IV" |> replace "LXXXX" "XC"
  104. 104. functionInput Output function Input 1 Output Input 2 Challenge #1: How can we compose these? 
  105. 105. Here is another challenge
  106. 106. function AInput Output function BInput Output 1 Output 2
  107. 107. function AInput Output function BInput Output 1 Output 2 Challenge #2: How can we compose these? 
  108. 108. COMPOSITION WITH BIND (FIZZBUZZ) Technique #3
  109. 109. 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.
  110. 110. 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 // helper function A simple F# implementation
  111. 111. 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 // helper function A simple F# implementation
  112. 112. Pipeline implementation number Handle 15 case
  113. 113. Pipeline implementation Handle 3 case number Handle 15 case
  114. 114. Pipeline implementation Handle 3 case Handle 5 case number Handle 15 case
  115. 115. Pipeline implementation Handle 3 case Handle 5 case number Answer Handle 15 case Last step
  116. 116. number Handle case Carbonated (e.g. "Fizz", "Buzz") Uncarbonated (e.g. 2, 7, 13)
  117. 117. Uncarbonated Carbonated Input -> or
  118. 118. Uncarbonated Carbonated Input -> type CarbonationResult = | Uncarbonated of int // unprocessed | Carbonated of string // "Fizz", Buzz", etc Idea from http://weblog.raganwald.com/2007/01/dont-overthink-fizzbuzz.html or
  119. 119. type CarbonationResult = | Uncarbonated of int // unprocessed | Carbonated of string // "Fizz", Buzz", etc 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
  120. 120. type CarbonationResult = | Uncarbonated of int // unprocessed | Carbonated of string // "Fizz", Buzz", etc let carbonate divisor label n = if (isDivisibleBy n divisor) then Carbonated label else Uncarbonated n
  121. 121. 12 |> carbonate 3 "Fizz" // Carbonated "Fizz" 10 |> carbonate 3 "Fizz" // Uncarbonated 10 10 |> carbonate 5 "Buzz" // Carbonated "Buzz" carbonate 5 "Buzz"
  122. 122. 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
  123. 123. 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
  124. 124. 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 // ... // ...
  125. 125. let fizzbuzz n = let result15 = n |> carbonate 15 "FizzBuzz" match result15 with | Carbonated str -> str | Uncarbonated n -> // do something with Uncarbonated value // ... // ...
  126. 126. if Carbonated then // return the string if Uncarbonated then // do something with the number
  127. 127. If Uncarbonated If Carbonated Bypass and return the string
  128. 128. let ifUncarbonatedDo f result = match result with | Carbonated str -> Carbonated str | Uncarbonated n -> f n
  129. 129. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  130. 130. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  131. 131. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  132. 132. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep
  133. 133. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep let lastStep result = match result with | Carbonated str -> str | Uncarbonated n -> string(n) // still not fizzy, so // convert to string
  134. 134. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> lastStep Composable => easy to extend
  135. 135. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> ifUncarbonatedDo (carbonate 7 "Baz") |> lastStep Composable => easy to extend
  136. 136. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> ifUncarbonatedDo (carbonate 7 "Baz") |> ifUncarbonatedDo (carbonate 11 "Pozz") |> lastStep Composable => easy to extend
  137. 137. let fizzbuzz n = n |> carbonate 15 "FizzBuzz" |> ifUncarbonatedDo (carbonate 3 "Fizz") |> ifUncarbonatedDo (carbonate 5 "Buzz") |> ifUncarbonatedDo (carbonate 7 "Baz") |> ifUncarbonatedDo (carbonate 11 "Pozz") |> ifUncarbonatedDo (carbonate 13 "Tazz") |> lastStep Composable => easy to extend
  138. 138. Another example: Chaining tasks
  139. 139. When task completesWait Wait a.k.a "promise", "future"
  140. 140. 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
  141. 141. 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 -> do something
  142. 142. let taskExample input = let taskX = startTask input taskX.WhenFinished (fun x -> let taskY = startAnotherTask x taskY.WhenFinished (fun y -> do something
  143. 143. let taskExample input = let taskX = startTask input taskX.WhenFinished (fun x -> do something
  144. 144. let whenFinishedDo f task = task.WhenFinished (fun taskResult -> f taskResult) let taskExample input = startTask input |> whenFinishedDo startAnotherTask |> whenFinishedDo startThirdTask |> whenFinishedDo ... Parameterize the next step
  145. 145. MONADS!
  146. 146. Is there a general solution to handling functions like this?
  147. 147. Yes! “Bind” is the answer! Bind all the things!
  148. 148. How do we compose these?
  149. 149. >> >> Composing one-track functions is fine...
  150. 150. >> >> ... and composing two-track functions is fine...
  151. 151.   ... but composing points/switches is not allowed!
  152. 152. Two-track input Two-track output One-track input Two-track output  
  153. 153. Two-track input Two-track output
  154. 154. Two-track input Two-track output A function transformer
  155. 155. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  156. 156. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  157. 157. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  158. 158. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  159. 159. let bind nextFunction result = match result with | Uncarbonated n -> nextFunction n | Carbonated str -> Carbonated str Two-track input Two-track output
  160. 160. 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 type CarbonationResult = | Uncarbonated of int | Carbonated of string
  161. 161. function AInput Output function BInput Output 1 Output 2 Challenge #2: How can we compose these? 
  162. 162. KLEISLI COMPOSITION (WEB SERVICE) Technique #4
  163. 163. = compose with The result is the same kind of thing Kleisli Composition
  164. 164. Async<HttpContext option>HttpContext A HttpHandler "WebPart"
  165. 165. Async<HttpContext option>HttpContext A HttpHandler "WebPart"
  166. 166. =>=> The result is another HttpHandler so you can keep adding and adding Composition of HttpHandlers Kleisli composition symbol
  167. 167. path "/hello" Checks request path (might fail) matches path doesn't match
  168. 168. OK "Hello" Sets response 200 OK
  169. 169. path "/hello" >=> OK "Hello" Checks request path (might fail) Sets response >=> A new WebPart
  170. 170. choose [ ] Picks first HttpHandler that succeeds
  171. 171. choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] Pick first path that succeeds
  172. 172. GET Only succeeds if request is a GET
  173. 173. GET >=> choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ]
  174. 174. let app = choose [ ] startWebServer defaultConfig app A complete web app GET >=> choose [ path "/hello" >=> OK "Hello" path "/goodbye" >=> OK "Goodbye" ] POST >=> choose [ path "/hello" >=> OK "Hello POST" path "/goodbye" >=> OK "Goodbye POST" ]
  175. 175. Http Response Http Request No classes, no inheritance, one-directional data flow!
  176. 176. Review • The philosophy of composition – Connectable, reusable parts • FP principles: – Composable functions – Composable types
  177. 177. Review A taste of various composition techniques: – Piping with "|>" – Currying/partial application – Composition using "bind" (monads!) – Kleisli composition using ">=>" Don't worry about understanding it all, but hopefully it's not so scary now!
  178. 178. Why bother? Benefits of composition: • Reusable parts – no strings attached • Testable – parts can be tested in isolation • Understandable – data flows in one direction • Maintainable – all dependencies are explicit • Extendable – can add new parts without touching old code • A different way of thinking – it's good for your brain to learn new things!
  179. 179. Slides and video here fsharpforfunandprofit.com/composition Thank you, DotNext! @ScottWlaschin Me on twitter My book Ask me anything about railways!

×