Slide 1

Slide 1 text

Robert Pickering

Slide 2

Slide 2 text

Programming is about data You have been fooled into thinking it’s about APIs

Slide 3

Slide 3 text

Functional Programming is not about programming without state

Slide 4

Slide 4 text

It is about succinctly describe the transformation between one state another, in a way that is composable.

Slide 5

Slide 5 text

Purity: only relying on data that’s passed as a parameter

Slide 6

Slide 6 text

The devil is in the detail Programming in the large Programming in the small versus

Slide 7

Slide 7 text

The Software Crisis Complexity Conformity Changeability Invisibility

Slide 8

Slide 8 text

Essential Complexity Accidental Complexity versus

Slide 9

Slide 9 text

Have you tried turning it off and on again?

Slide 10

Slide 10 text

Command-line App File File Service/Daemon Network I/O Network I/O GUI App User Input Image Data The only three apps you’ll ever write …

Slide 11

Slide 11 text

Long running app statement oriented style Process Event Environment

Slide 12

Slide 12 text

Long running app expression oriented style Process Event Environment

Slide 13

Slide 13 text

Expressions vs Statements An expression is something that evaluates to value A statement is everything else

Slide 14

Slide 14 text

If in a statement oriented language string firstArg = null; if (args.Length > 0) { firstArg = args[0]; }

Slide 15

Slide 15 text

If in a expression oriented language let firstArg = if argv.Length > 1 then argv.[0] else ""

Slide 16

Slide 16 text

What about the ternary operator? string firstArg = (args.Length > 0) ? args[0] : "";

Slide 17

Slide 17 text

There are limits to the ternary operator let firstArg = if argv.Length > 1 then let argString = argv.[0] argString.Trim() else ""

Slide 18

Slide 18 text

If statement with two values string firstArg = null; string secondArg = null; if (args.Length > 1) { firstArg = args[0]; secondArg = args[1]; }

Slide 19

Slide 19 text

If expression with two values let firstArg, secondArg = if argv.Length > 1 then argv.[0], argv.[1] else "", ""

Slide 20

Slide 20 text

Data gets trapped inside statements and needs to be pushed to a specific location in the outside world

Slide 21

Slide 21 text

Lock statement static object sync = new object(); static string[] items = new string[] { "1", "2", "3" }; static void DoItemEx5(int index) { string myDataPoint = null; lock (sync) { myDataPoint = items[index]; } // ... }

Slide 22

Slide 22 text

Lock expression/function let sync = new obj() let items = [| "1"; "2"; "3"|] let DoItemEx5 index = let myDataPoint = lock sync (fun () -> items.[index]) // ...

Slide 23

Slide 23 text

Using statement string myDataPoint = null; using (var conn = new SqlConnection()) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "select field from table"; myDataPoint = (string)cmd.ExecuteScalar(); } }

Slide 24

Slide 24 text

use binding use conn = new SqlConnection() conn.Open(); use cmd = conn.CreateCommand(CommandText = "select field from table") let myDataPoint = cmd.ExecuteScalar() :?> string

Slide 25

Slide 25 text

Statement oriented programming emphasizes reading and write, and so creates coupling with the points being read from/written to

Slide 26

Slide 26 text

Expression oriented programming emphasizes the calculations and transformation between one value and another.

Slide 27

Slide 27 text

The process of refactoring towards purity generally involves disentangling computation from the environment it operates in, which almost invariably means more parameter passing John Carmack Functional Programming in C++

Slide 28

Slide 28 text

Ways to get data into a function 1. Read the data from somewhere 2. Have the caller pass the data to you as a parameter

Slide 29

Slide 29 text

The inconvenience of parameters let bigFunc thing1 otherThing thirdItem moreStuff gettingBordNow engoughAlready = // ...

Slide 30

Slide 30 text

Nesting functions reduce need for parameters let bigFunc' thing1 otherThing thirdItem moreStuff gettingBordNow engoughAlready = let doSomething1 index = (thing1 + index) - thirdItem let doSomething2 index = (thing1 + otherThing) * moreStuff doSomething1 1 |> doSomething2 // ...

Slide 31

Slide 31 text

class BadClass { int thing1; int otherThing; int thirdItem; int moreStuff; int gettingBordNow; int engoughAlready; int DoSomething1(int index) { return (thing1 + index) - thirdItem; } int DoSomething2(int index) { return (thing1 + otherThing) * moreStuff; } public int BigFunc(int thing1, int otherThing, int thirdItem, int moreStuff, int gettingBordNow, int engoughAlready) { // ... unpack fields ... return DoSomething2(DoSomething1(1)); } }

Slide 32

Slide 32 text

Nested functions offer better protection than private fields let hardWorkingFunc environment = let buffer = Array.zeroCreate 255: int[] let doWork1 parameter = // do some more work with buffer () let doWork2 parameter = for x in buffer do doWork1 x buffer

Slide 33

Slide 33 text

class HardWorkingClass { int[] buffer = new int[255]; private void DoWork1(int parameter) { // do some more work with buffer } private void DoWork2(int parameter) { foreach (var x in buffer) { DoWork1(x); } } public int[] HardWorkingFunction() { DoWork2(1); return buffer; } } // game over!

Slide 34

Slide 34 text

The problem with fields

Slide 35

Slide 35 text

public class Stuff { } public interface IRepository { Stuff[] GetStuff(); } public class MockableClass { IRepository _repository; public MockableClass(IRepository repository) { _repository = repository; } // ... } A word on mocking …

Slide 36

Slide 36 text

Testability: Take a dependencies on anything other than the data pasted to you, and the test will need to ensure those dependencies are correctly initialized

Slide 37

Slide 37 text

.NET Type System The .NET type system guarantees that a reference either points to valid instance or null

Slide 38

Slide 38 text

NullReferenceException

Slide 39

Slide 39 text

F# Type System The F# type system guarantees that a reference either points to valid instance or null (or at least tries very hard to)

Slide 40

Slide 40 text

Tuples let tuple = "first", 2 let first, two = tuple

Slide 41

Slide 41 text

Record Types type Person = { FirstName: string; LastName: string; }

Slide 42

Slide 42 text

Record Types let person = { FirstName = "Robert"; LastName = "Pickering"; } let person' = { person with LastName = "Townson"; }

Slide 43

Slide 43 text

Union Types type Choice<'a, 'b> = | Choice1 of 'a | Choice2 of 'b

Slide 44

Slide 44 text

Union Types let aChoice = Choice1 "made" let whichChoice = match aChoice with | Choice1 value -> sprintf "Choice1 %A" value | Choice2 value -> sprintf "Choice2 %A" value

Slide 45

Slide 45 text

45 // pipe forward let (|>) x f = f x Pipe Forward

Slide 46

Slide 46 text

46 Pipe Forward let items = [ 1 .. 10 ] let squares = Seq.map (fun x -> x * x) (Seq.filter (fun x -> x > 3) items) let squares' = items |> Seq.filter (fun x -> x > 3) |> Seq.map (fun x -> x * x)

Slide 47

Slide 47 text

47 // compose let (>>) f g x = g (f x) Compose let compose func1 func2 value = let result = func1 value func2 result

Slide 48

Slide 48 text

48 let length (s: string) = s.Length let makeString i = new String('a', i) let combinedFunction = length >> makeString combinedFunction "1234" // result "aaaa" Compose

Slide 49

Slide 49 text

An LOB app using composition

Slide 50

Slide 50 text

type Request = { name:string; email:string } A type to hold the data

Slide 51

Slide 51 text

A type to represent success or failure // the two-track type type Result<'TSuccess,'TFailure> = | Success of 'TSuccess | Failure of 'TFailure

Slide 52

Slide 52 text

let validate1 input = if input.name = "" then Failure "Name must not be blank" else Success input let validate2 input = if input.name.Length > 50 then Failure "Name must not be longer than 50 chars" else Success input let validate3 input = if input.email = "" then Failure "Email must not be blank" else Success input Validation functions

Slide 53

Slide 53 text

bind let bind inputFunction inputValue = match inputValue with | Success s -> inputFunction s | Failure f -> Failure f

Slide 54

Slide 54 text

bind let combindedValidate = let validate1' = bind validate1 let validate2' = bind validate2 let validate3' = bind validate3 validate1' >> validate2' >> validate3' let altCombindedValidate = bind validate1 >> bind validate2 >> bind validate3

Slide 55

Slide 55 text

plus let plus addSuccess addFailure switch1 switch2 x = match (switch1 x),(switch2 x) with | Failure f1,Success _ -> Failure f1 | Success _ ,Failure f2 -> Failure f2 | Success s1,Success s2 -> Success (addSuccess s1 s2) | Failure f1,Failure f2 -> Failure (addFailure f1 f2)

Slide 56

Slide 56 text

plus let combineFailures v1 v2 = let addSuccess r1 r2 = r1 // return first let addFailure s1 s2 = s1 + "; " + s2 // concat plus addSuccess addFailure v1 v2

Slide 57

Slide 57 text

plus let combinedValidation = validate1 |> combineFailures validate2 |> combineFailures validate3

Slide 58

Slide 58 text

Normalizing the data let canonicalizeEmail input = { input with email = input.email.Trim().ToLower() }

Slide 59

Slide 59 text

map let map f inputValue = match inputValue with | Success s -> Success (f s) | Failure f -> Failure f

Slide 60

Slide 60 text

map let usecase = combinedValidation >> map canonicalizeEmail

Slide 61

Slide 61 text

let updateDatabase input = () // dummy dead-end function for now Updating the database

Slide 62

Slide 62 text

tee let tee f x = f x; x

Slide 63

Slide 63 text

let tryCatch f exnHandler x = try Success(f x) with | ex -> Failure(exnHandler ex) tryCatch

Slide 64

Slide 64 text

let updateDatebaseStep request = tryCatch (tee updateDatabase) (fun ex -> ex.Message) request Converted db function

Slide 65

Slide 65 text

Completed Usecase let usecase = combinedValidation >> map canonicalizeEmail >> bind updateDatebaseStep

Slide 66

Slide 66 text

let mainCompile (argv,bannerAlreadyPrinted,exiter:Exiter,createErrorLogger) = // Don's note: "GC of intermediate data is really, really important here" main1 (argv,bannerAlreadyPrinted,exiter,createErrorLogger) |> main2 |> main2b |> main2c |> main3 |> main4 In the real world …

Slide 67

Slide 67 text

With a big thanks to … Railway oriented programming http://fsharpforfunandprofit.com/posts/recipe-part2/

Slide 68

Slide 68 text

Further Reading

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Questions?