Upload
jack-fox
View
284
Download
0
Embed Size (px)
DESCRIPTION
Functional programming for production quality code
Citation preview
Functional Programming for Production Quality Code
Jack Foxjackfoxy.com craftyThoughts
@foxyjackfox
Slideshttp://www.slideshare.net/jackfoxy
Sample Codehttps://github.com/jackfoxy/Svcc2014Demo
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
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()
Functional Programming
Remember functions from high school math?
f(x) = y
one inputparameter
always mapsto the same
output
Functional Programming
But I need multiple input parameters!
Remember functions from high school math?
f(x) = y
one inputparameter
always mapsto the same
output
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
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”
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
Next: a short digression into types
These are all types
o int
o string
o list
o WellState
o Exception
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
Generic types
type Choice<'T1, 'T2> =
| Success of 'T1
| Failure of 'T2
generic types in type constructor
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)
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
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)
The tools to pipeline a production process
o Partial application
o Summation type / Coproduct type / Discriminated Union (F#)
o Pattern matching
o Generic types
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
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
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
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
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
Function Composition
f(x') = w'
g(x'') = w''
h(x''') = w'''h( g( f(x) ) ) = w
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
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
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?
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
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…
Async (it’s just another computation expression)
async {
}
The brackets abstracted away a layer of complexity …
…but at a price.*
* We lose function composition.
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
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
}
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
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
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
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
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
SQL Enumeration
type ProductCategory = SqlEnumProvider<"
SELECT Name, ProductCategoryID
FROM Production.ProductCategory", connectionString>
let AccessoriesId = ProductCategory.Accessories
intellisense dot
completion
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
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