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.

Functional programming for production quality code

1,234 views

Published on

Functional programming for production quality code

Published in: Software

Functional programming for production quality code

  1. 1. Functional Programming for Production Quality Code Jack Fox jackfoxy.com  craftyThoughts @foxyjackfox Slides http://www.slideshare.net/jackfoxy Sample Code https://github.com/jackfoxy/Svcc2014Demo
  2. 2. Functional Programming for Production Quality Code o Success / Failure passing o Partial Application o Types, Summation Type, Generic Types o Pattern Matching o Modadic Bind o Function Composition o Units of Measure (design time types) o Computation Expressions o Typing your way into relational data
  3. 3. Ever seen this code? if condition1 then doThis() if condition2 then doThat() if condition3 then doSomeOtherThing() if condition3 then aTask() if condition4 then anotherTask() if condition5 then yetAnotherTask() if condition5 then finallyDone() else handleError() else handleError() else handleError() else handleError() else handleError() else handleError() else handleError()
  4. 4. Functional Programming Remember functions from high school math? f(x) = y one input parameter always maps to the same output
  5. 5. Functional Programming Remember functions from high school math? f(x) = y one input parameter always maps to the same output But I need multiple input parameters!
  6. 6. A compiler FP trick called “currying” let f x y z = let w = 1 … //do something w //return f(x g(y h(z))) = w wrapping single parameter functions within functions f: x -> (y -> (z -> w)) function signature
  7. 7. o You don’t really need to know currying o Instead understand the inverse of currying o Unwrap the parameters of a function o Unwrapping is called “Partial Application”
  8. 8. Partial application o If this is a function let steamJob temperature barrelsOfSteam wellsToSteam = … somethingWeReturn steamJob : temperature: int -> barrelsOfSteam: int -> wellsToSteam: WellState list -> int o Then so is this let mySteamJob = steamJob 500 10000 mySteamJob : (WellState list -> int)
  9. 9. Next: a short digression into types These are all types o int o string o list o WellState o Exception
  10. 10. Summation type o This is a type that takes the form of multiple types o It can only be one of those types at a time o In F# this type is called “discriminated union” type Choice<'T1, 'T2> = | Success of 'T1 | Failure of 'T2 note this in not the actual Choice type in Fsharp.Core
  11. 11. Generic types generic types in type constructor type Choice<'T1, 'T2> = | Success of 'T1 | Failure of 'T2
  12. 12. Return Summation Type (Discriminated Union) let steamJob2 (temperature :int) (barrelsOfSteam : int) (wellsToSteam : WellState list) = … if … then Success wellsToSteam else Failure (BadSteamJob (sprintf "BadSteamJob temperature %i barrelsOfSteam %i wellsToSteam %A“ temperature barrelsOfSteam wellsToSteam) :> Exception )
  13. 13. Return Summation Type (Discriminated Union) let steamJob2 (temperature :int) (barrelsOfSteam : int) (wellsToSteam : WellState list) = … if … then Success wellsToSteam else Failure (BadSteamJob (sprintf "BadSteamJob temperature %i barrelsOfSteam %i wellsToSteam %A“ temperature barrelsOfSteam wellsToSteam) :> Exception ) steamJob : temperature:int -> barrelsOfSteam:int -> wellsToSteam:WellState list -> Choice<WellState list, Exception> generic constructor types replaced with real types
  14. 14. let result = steamJob2 500 4000 wellsToSteam match result with | Success steamedWells -> printfn "%A" steamedWells | Failure exn -> printfn "%s" exn.Message steamJob : temperature:int -> barrelsOfSteam:int -> wellsToSteam:WellState list -> Choice<WellState list, Exception> Consuming a Summation Type (Discriminated Union)
  15. 15. The tools to pipeline a production process o Partial application o Summation type / Coproduct type / Discriminated Union (F#) o Pattern matching o Generic types
  16. 16. The tools to pipeline a production process o Partial application o Summation type / Coproduct type / Discriminated Union (F#) o Pattern matching o Generic types o Monadic Bind
  17. 17. Monadic Bind let bind nextFunction lastOutput = match lastOutput with | Success s -> nextFunction s | Failure f -> Failure f bind : nextFunction : ('a -> Choice<'b,'c>) -> lastOutput : Choice<'a,'c> -> Choice<'b,'c> generic constructor types to be replaced with real types
  18. 18. Monadic Bind let bind nextFunction lastOutput = match lastOutput with | Success s -> nextFunction s | Failure f -> Failure f the generics line up between nextFunction and lastOutput bind : nextFunction:('a -> Choice<'b,'c>) -> lastOutput:Choice<'a,'c> -> Choice<'b,'c> resulting output lets us chain indefinitely
  19. 19. The tools to pipeline a production process o Partial application o Summation type / Coproduct type / Discriminated Union (F#) o Pattern matching o Generic types o Monadic Bind
  20. 20. The tools to pipeline a production process o Partial application o Summation type / Coproduct type / Discriminated Union (F#) o Pattern matching o Generic types o Monadic Bind o Function Composition
  21. 21. Function Composition f(x') = w' g(x'') = w'' h(x''') = w''' h( g( f(x) ) ) = w
  22. 22. Function Composition let funcOne x = funcOne : 'T1 -> 'T2 let funcTwo x = funcTwo : 'T2 -> 'T3 let compose = funcOne >> funcTwo ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3 compose: 'T1 -> 'T3
  23. 23. Putting it all together let processWells = steamJob step1Temp step1Bbl >> bind (acidJob step2Solution step2Bbl) >> bind (steamJob step3Temp step3Bbl) >> bind (acidJob step4Solution step4Bbl) let run() = match processWells wellsToSteam with | Success xs -> printfn "success %A" xs | Failure exn -> printfn "%s" exn.Message
  24. 24. What could we do better? o Is there another step we could take to promote correctness? o There are a lot of parameters. o Might a programmer easily mix them up in the source code?
  25. 25. Units of Measure o Design time types over any numeric type o Types check at compile time [<Measure>] type degF // degrees Fahrenheit [<Measure>] type pct // percent [<Measure>] type bbl // barrels [<Measure>] type acidId // acid Id let step1Temp = 500<degF> let step1Bbl = 10000<bbl> let step2Solution = 25.0<pct> let step2Bbl = 20<bbl> let steamJob (temp :int<degF>) (bblOfSteam : int<bbl>) (wells : WellState list) =
  26. 26. So Far… o Success / Failure passing o Partial Application o Types, Summation Type, Generic Types o Pattern Matching o Modadic Bind o Function Composition o Units of Measure (design time types)
  27. 27. Async (it’s just another computation expression) async { } The brackets abstracted away a layer of complexity … …but at a price.* *We lose function composition.
  28. 28. Async (it’s frequently about plumbing) async { } http://commons.wikimedia.org/wiki/File:PSM_V33_D306_Plumbing_arrangement_in_a_19th_century_new_york_house.jpg
  29. 29. let productModel name = async { let! result = asyncChoice { let! productId = productIdByName name let! prodDescription = productAndDescription productI let containsFoo = prodDescription.Description.Contains("foo") if containsFoo then return! async { return Failure ( OutOfBounds("Don't show customers foo")) } else let descriptionWords = prodDescription.Description.Split(" ".ToCharArray()) let! productId2 = productIdByName descriptionWords.[0] let! prodDescription2 = productAndDescription productId2 return prodDescription2.ProductModel } match result with | Success x -> return Success x | Failure exn -> return Failure ex }
  30. 30. AsyncChoicebuilder * member __.Bind … member __.Combine (r1, r2) : Async<Choice<'T, 'Error>> = async { let! r1' = r1 match r1' with | Choice1Of2 () -> return! r2 | Choice2Of2 error -> return Choice2Of2 error } * ExtCore
  31. 31. asyncChoice { let! productId = productIdByName name let! prodDescription = productAndDescription productI let containsFoo = prodDescription.Description.Contains("foo") if containsFoo then return! async { return Failure ( OutOfBounds("Don't show customers foo")) } else let descriptionWords = prodDescription.Description.Split(" ".ToCharArray()) let! productId2 = productIdByName descriptionWords.[0] let! prodDescription2 = productAndDescription productId2 return prodDescription2.ProductModel } binds to value inside the builder returns the value inside the computation expression choice builder’s combine composes bound values inside “choice” “inbetween” regular F# syntax and control statements
  32. 32. Typing your way into relational data o FSharp.Data.SqlClient type ProductIdByName = SqlCommandProvider<" SELECT ProductID from Production.Product WHERE Name = @name ", connectionString, SingleRow = true> write any T-SQL inline, “red squigglies” from compiler on syntax and schema errors optional parameter
  33. 33. FSharp.Data.SqlClient let productIdByName name = async { let! result = async { return type is a record strongly typed to result of query use cmd = new ProductIdByName() return! cmd.AsyncExecute(name = name) } |> Async.Catch parameters indicated by intellisense in this case return type is option match result with | Choice1Of2 (Some productID) -> return Success productID | Choice1Of2 _ -> return Failure ( SelectNotFound() :> Exception ) | Choice2Of2 exn -> return Failure exn
  34. 34. Programmability over functions and sprocs [<Literal>] let connectionString = @"Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=SSPI" type AdventureWorks = SqlProgrammabilityProvider<connectionString> type Dbo = AdventureWorks.dbo type BillOfMaterials = AdventureWorks.dbo.uspGetBillOfMaterials let cmd = new BillOfMaterials() cmd.AsyncExecute(1, DateTime.UtcNow) |> Async.RunSynchronously return type strongly typed to result parameters indicated by intellisense intellisense dot completion
  35. 35. SQL Enumeration type ProductCategory = SqlEnumProvider<" SELECT Name, ProductCategoryID FROM Production.ProductCategory", connectionString> let AccessoriesId = ProductCategory.Accessories intellisense dot completion
  36. 36. What we covered o Success / Failure passing o Partial Application o Types, Summation Type, Generic Types o Pattern Matching o Modadic Bind o Function Composition o Units of Measure (design time types) o Computation Expressions o Typing your way into relational data
  37. 37. Questions? Bibliography o Railway oriented programming http://fsharpforfunandprofit.com/posts/recipe-part2/ o Computation Expressions http://msdn.microsoft.com/en-us/library/dd233182.aspx o The F# Computation Expression Zoo http://tomasp.net/academic/papers/computation-zoo/computation-zoo.pdf o ExtCore, an extended core library for F# https://github.com/jack-pappas/ExtCore o FSharp.Data.SqlClient http://fsprojects.github.io/FSharp.Data.SqlClient/ Code: github.com/jackfoxy/Svcc2014Demo Slides: www.slideshare.net/jackfoxy

×