Reasonable Code With Fsharp

Post on 17-Dec-2014

568 views 2 download

Tags:

description

 

Transcript of Reasonable Code With Fsharp

embrace the paradigm

REASONABLE CODE WITH F#

ABOUT ME

@mfalanga

me@michaelfalanga.com

SECTION: OVERVIEWHelp the human

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#

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#

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'

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#

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#

IMMUTABILITY

BENEFITS

DRAWBACKS

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

Intended side effects are necessaryPerformance

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 '!'.

OVERVIEW

1.Human readable2.Reusability3.Sensible defaults

FUNCTIONS AND FUNCTIONAL TYPES

Which problem would you rather focus on?

a) Where the code should live

b) What the code should do

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

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#

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

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

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#

OPTION TYPE

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

F#

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#

FUNCTIONS AND FUNCTIONAL TYPES

1.Think small, build big2.Model3.Flow

COMPILER CHECKED CORRECTNESS

Step 1: Create types

Step 2: Lean on the compiler

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#

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#

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#

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#

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

COMPILER CHECKED CORRECTNESS

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

SUMMARY

To Reason: To make sense of

Syntax & IdiomsFunctional compositionCompiler-checked correctness