37
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

Functional programming for production quality code

Embed Size (px)

DESCRIPTION

Functional programming for production quality code

Citation preview

Page 1: Functional programming for production quality code

Functional Programming for Production Quality Code

Jack Foxjackfoxy.com craftyThoughts

@foxyjackfox

Slideshttp://www.slideshare.net/jackfoxy

Sample Codehttps://github.com/jackfoxy/Svcc2014Demo

Page 2: Functional programming for production quality code

Functional Programming for Production Quality CodeType

do 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

Page 3: Functional programming for production quality code

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

Page 4: Functional programming for production quality code

Functional Programming

Remember functions from high school math?

f(x) = y

one inputparameter

always mapsto the same

output

Page 5: Functional programming for production quality code

Functional Programming

But I need multiple input parameters!

Remember functions from high school math?

f(x) = y

one inputparameter

always mapsto the same

output

Page 6: Functional programming for production quality code

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

f: x -> (y -> (z -> w))

wrapping single parameter functionswithin functions

function signature

Page 7: Functional programming for production quality code

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”

Page 8: Functional programming for production quality code

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)

Partial application

Page 9: Functional programming for production quality code

Next: a short digression into types

These are all types

o int

o string

o list

o WellState

o Exception

Page 10: Functional programming for production quality code

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 'T2note this in not the actual Choice type in Fsharp.Core

Page 11: Functional programming for production quality code

Generic types

type Choice<'T1, 'T2> =

| Success of 'T1

| Failure of 'T2

generic types in type constructor

Page 12: Functional programming for production quality code

Return Summation Type

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 )

(Discriminated Union)

Page 13: Functional programming for production quality code

Return Summation Type

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 )

(Discriminated Union)

steamJob : temperature:int -> barrelsOfSteam:int -> wellsToSteam:WellState list ->Choice<WellState list, Exception>

generic constructor types replaced with real types

Page 14: Functional programming for production quality code

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)

Page 15: Functional programming for production quality code

The tools to pipeline a production process

o Partial application

o Summation type / Coproduct type / Discriminated Union (F#)

o Pattern matching

o Generic types

Page 16: Functional programming for production quality code

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

Page 17: Functional programming for production quality code

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

Page 18: Functional programming for production quality code

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>

the generics line up between nextFunction and lastOutput

resulting output lets us chain indefinitely

Page 19: Functional programming for production quality code

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

Page 20: Functional programming for production quality code

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

Page 21: Functional programming for production quality code

Function Composition

f(x') = w'

g(x'') = w''

h(x''') = w'''h( g( f(x) ) ) = w

Page 22: Functional programming for production quality code

let funcOne x =

let compose = funcOne >> funcTwo

let funcTwo x =

Function Composition

funcTwo : 'T2 -> 'T3

funcOne : 'T1 -> 'T2

( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3

compose: 'T1 -> 'T3

Page 23: Functional programming for production quality code

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

Page 24: Functional programming for production quality code

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?

Page 25: Functional programming for production quality code

Units of Measureo Design time types over any numeric

type

[<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) =

o Types check at compile time

Page 26: 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)

So Far…

Page 27: Functional programming for production quality code

Async (it’s just another computation expression)

async {

}

The brackets abstracted away a layer of complexity …

…but at a price.*

* We lose function composition.

Page 28: Functional programming for production quality code

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

Page 29: Functional programming for production quality code

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

}

Page 30: Functional programming for production quality code

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

Page 31: Functional programming for production quality code

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

Page 32: Functional programming for production quality code

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

Page 33: Functional programming for production quality code

FSharp.Data.SqlClient

let productIdByName name = async {

let! result =

async {

use cmd = new ProductIdByName()

return! cmd.AsyncExecute(name = name)

}

|> Async.Catch

match result with

| Choice1Of2 (Some productID) -> return Success productID

| Choice1Of2 _ -> return Failure ( SelectNotFound() :> Exception )

| Choice2Of2 exn -> return Failure exn

return type is a record strongly

typed to result of query

parameters indicated by intellisense

in this case return type is option

Page 34: Functional programming for production quality code

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.RunSynchronouslyreturn type strongly typed to

result

parameters indicated by intellisense

intellisense dot

completion

Page 35: Functional programming for production quality code

SQL Enumeration

type ProductCategory = SqlEnumProvider<"

SELECT Name, ProductCategoryID

FROM Production.ProductCategory", connectionString>

let AccessoriesId = ProductCategory.Accessories

intellisense dot

completion

Page 36: 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

What we covered

Page 37: Functional programming for production quality code

Questions?Bibliographyo Railway oriented programming

http://fsharpforfunandprofit.com/posts/recipe-part2/

o Computation Expressionshttp://msdn.microsoft.com/en-us/library/dd233182.aspx

o The F# Computation Expression Zoohttp://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.SqlClienthttp://fsprojects.github.io/FSharp.Data.SqlClient/

Code: github.com/jackfoxy/Svcc2014Demo Slides: www.slideshare.net/jackfoxy