Reasonable Code With Fsharp

29
embrace the paradigm REASONABLE CODE WITH F#

description

 

Transcript of Reasonable Code With Fsharp

Page 1: Reasonable Code With Fsharp

embrace the paradigm

REASONABLE CODE WITH F#

Page 2: Reasonable Code With Fsharp

ABOUT ME

@mfalanga

[email protected]

Page 3: Reasonable Code With Fsharp

SECTION: OVERVIEWHelp the human

Page 4: Reasonable Code With Fsharp

SYNTAXint euler1(int max) { var total = 0; for(var n=1; n<max; n++) { if(n % 3 == 0 || n % 5 == 0){ total += n; } }

return total;}

euler1(1000);

C#

let euler1 max = [1..max-1] |> List.filter (fun n -> n % 3 = 0 || n % 5 = 0) |> List.sum

euler1 1000

F#

Page 5: Reasonable Code With Fsharp

SYNTAX – VS LINQ

int euler1(int max) {return Enumerable.Range(1, max - 1) .Where(n => n % 3 == 0 || n % 5 == 0) .Sum();}

euler1(1000);

C#

let euler1 max = [1..max-1] |> List.filter (fun n -> n % 3 = 0 || n % 5 = 0) |> List.sum

euler1 1000

F#

Page 6: Reasonable Code With Fsharp

TYPE INFERENCE

STRONGSTATIC

int x = 3;long y = 4;

var z = x + y; // = 7

C# let x = 3let y:int64 = 4L

x + y

F#

The type 'int64' does not match the type 'int'

Page 7: Reasonable Code With Fsharp

AUTOMATIC GENERALIZATION

val firstInGroup : g:('a -> 'b) -> list:seq<'a> -> seq<'b * 'a> when 'b : equality

public IEnumerable<Tuple<U,T>> firstInGroup<T,U> (Func<T,U> g, IEnumerable<T> list) { return list.GroupBy(g) .Select(grp => new Tuple<U, T>(grp.Key, grp.First())); }

C#

let firstInGroup g list = list |> Seq.groupBy g |> Seq.map (fun (key,items) -> key, Seq.head items)

F#

Page 8: Reasonable Code With Fsharp

GENERALIZE THIS!

int add<T>(T x, T y) where T: op_addition {return x + y;

}

C#

let add x y = x + y

add 3 4add 4.2 5.1

F# let inline add x y = x + y

add 3 4add 4.2 5.1add "hello" "World"

F#

Page 9: Reasonable Code With Fsharp

IMMUTABILITY

BENEFITS

DRAWBACKS

Eliminate unintended side effectsSets you up for multi-core programmingDebugging is easierTestability is higher

Intended side effects are necessaryPerformance

Page 10: Reasonable Code With Fsharp

IMMUTABILITY - EXAMPLE

Example from “Inside F#” blog

let actions = List.init 5 (fun i -> fun() -> i*2) for act in actions do printf "%d " (act())

F#

List<Func<int>> actions = new List<Func<int>>(); for (int i = 0; i < 5; ++i) { actions.Add( () => i * 2 ); }

foreach (var act in actions) { Console.WriteLine( act() ); }

C#

The mutable variable 'i' is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'.

Page 11: Reasonable Code With Fsharp

OVERVIEW

1.Human readable2.Reusability3.Sensible defaults

Page 12: Reasonable Code With Fsharp

FUNCTIONS AND FUNCTIONAL TYPES

Which problem would you rather focus on?

a) Where the code should live

b) What the code should do

Page 13: Reasonable Code With Fsharp

COMPOSITION VS INHERITANCE

Is-a relationship that extends the base classVery high couplingSubclasses may not need all functionality from base classSubclass has to be aware of the base class’s implementation

Inheritance

Composition

Ninject StructureMap

Castle WindsorUnityInterface-dependent

Page 14: Reasonable Code With Fsharp

FUNCTION COMPOSITIONPipeline Operator: |>Return value of first function becomes the last parameter of second function

Forward Composition Operator: >>Create new functions that are sequences of existing functions

webUrl |> downloadPage |> extractMetaInfo |> categorizeResource

F#

let categorizeUrl = downloadPage >> extractMetaInfo >> categorizeResource

let categorizeEmail = parseEmail >> extractDetail >> categorizeResource

categorizeUrl webUrlcategorizeEmail email

F#

Page 15: Reasonable Code With Fsharp

FUNCTION COMPOSITION

Partial ApplicationCreate new functions by supplying some of the arguments to an existing function

let MSBuild properties (outputPath:string) (targets:string) = //Do some msbuild stuff

let MSBuildDebug = MSBuild ["Configuration","Debug"]

let MSBuildRelease = MSBuild ["Configuration","Release"]

F#

* From FAKE

Page 16: Reasonable Code With Fsharp

PATTERN MATCHING

let data = ("Cleveland", 390000)let city, population = data

F#

let x = 9match x with | num when num < 10 -> printfn "Less than ten" | _ -> printfn "Greater than or equal to ten"

F#

DECOMPOSE

COMPARE

Page 17: Reasonable Code With Fsharp

DISCRIMINATED UNIONS

type Shape = | Square of int | Rectangle of float*float | Circle of float

F#

let getArea shape = match shape with | Square side -> float(side * side) | Rectangle(w,h) -> w * h | Circle r -> System.Math.PI * r * r

F#

let sq = Square 7let rect = Rectangle 2.2 3.3let cir = Circle 3.4

F#

Page 18: Reasonable Code With Fsharp

OPTION TYPE

type Option<‘T> = | None | Some of ‘T

F#

Page 19: Reasonable Code With Fsharp

OBJECT MODELING

* From F# Deep Dives

type MarkdownDocument = list<MarkdownBlock>

and MarkdownBlock = | Heading of int * MarkdownSpans | Paragraph of MarkdownSpans | CodeBlock of list<string>

and MarkdownSpans = list<MarkdownSpan>

and MarkdownSpan = | Literal of string | InlineCode of string | Strong of MarkdownSpans | Emphasis of MarkdownSpans | Hyperlink of MarkdownSpans * string

F#

Page 20: Reasonable Code With Fsharp

FUNCTIONS AND FUNCTIONAL TYPES

1.Think small, build big2.Model3.Flow

Page 21: Reasonable Code With Fsharp

COMPILER CHECKED CORRECTNESS

Step 1: Create types

Step 2: Lean on the compiler

Page 22: Reasonable Code With Fsharp

UNITS OF MEASURE

unitsOfMeasure.fsx(11,19): error FS0001: Type mismatch. Expecting a int<mi> [] but given a int<km> [] The unit of measure 'mi' does not match the unit of measure 'km'

[<Measure>] type mi

[<Measure>] type km

// define some valueslet mike = [| 6<mi>; 9<mi>; 5<mi>; 18<mi> |]let chris = [| 3<km>; 5<km>; 2<km>; 8<km> |]

let totalDistance = (Array.append mike chris) |> Array.sum

F#

Page 23: Reasonable Code With Fsharp

UNITS OF MEASURE

enum DistanceUnit { Miles, Kilometers}

class Run { private float distance; private DistanceUnit unit;

public Run(float distance, DistanceUnit unit){ this.distance = distance; this.unit = unit; }}

C#

Page 24: Reasonable Code With Fsharp

EXHAUSTIVE PATTERN MATCHING//Modelmodule Person = type T = Person of string

let create name = if String.IsNullOrWhiteSpace(name) then None else Some(Person name)

let value (Person p) = p

//DALlet save person = //put the person in the database… Some 42

//UIlet readInput name = match Person.create name with | None -> printfn "Please supply a name" | Some p -> match save p with | None -> printfn "An error occurred" | Some id -> printfn "The id for %s is %d" (Person.value p) id

F#

Page 25: Reasonable Code With Fsharp

EXHAUSTIVE PATTERN MATCHING//DALtype DatabaseResult<'a> = | Success of 'a | UniqueViolation | GeneralException of Exception

//UIlet readInput name = match Person.create name with | None -> printfn "Please supply a name" | Some p -> match save p with | Success(id) -> printfn "The id for %s is %d" (Person.value p) id | UniqueViolation -> printfn "The name %s already exists" (Person.value p) | GeneralException(ex) -> printfn "%s" ex.Message

F#

Page 26: Reasonable Code With Fsharp

DATABASE ACCESS, TOO?

1. Reference type provider

2. Provide type provider connection

3. GO!

1. Add EF template

2. Give details about connection

3. Decide what to import

4. Wait for files to be generated

5. GO!

Entity FrameworkType Provider

Amount of code added to project

Page 27: Reasonable Code With Fsharp

COMPILER CHECKED CORRECTNESS

1.Focus on the problem2.Don’t forget stuff

Page 28: Reasonable Code With Fsharp

SUMMARY

To Reason: To make sense of

Syntax & IdiomsFunctional compositionCompiler-checked correctness