28
@kitlovesfsharp www.github.com/misterspeedy F# For an Easy Life! Kit Eason

F# Presentation for SmartDevs, Hereford

Embed Size (px)

DESCRIPTION

An intro to F# done originally for SmartDevs, Hereford

Citation preview

Page 1: F# Presentation for SmartDevs, Hereford

@kitlovesfsharp www.github.com/misterspeedy

F# For an Easy Life!

Kit Eason

Page 2: F# Presentation for SmartDevs, Hereford

F# in a nutshell

Microsoft first class supported language for .NET

Supports OO and functional paradigms

Compiles to CLI like C#, VB.Net

First class citizen in VS2010 and VS2012 (free!). (Also in 2013 preview)

Open source, runs on Mono (use Xamarin)

Strongly typed

Page 3: F# Presentation for SmartDevs, Hereford

F# functions

Use ‘let’ to declare functions and values

let add x y =

x + y

The return value of the function is the last value calculated (no ‘return’

statement

let add x y =

x + y

Types are inferred (at design time)

Argument lists don’t have brackets

Page 4: F# Presentation for SmartDevs, Hereford

F# Interactive (FSI)

> let add x y =

x + y;;

val add : x:int -> y:int -> int

> add 3 4;;

val it : int = 7

>

• Use F# interactive to define and try out functions

Page 5: F# Presentation for SmartDevs, Hereford

Calculating present value

> let presentValue fv r t =

fv * (1.+r) ** (-t);;

val presentValue : fv:float -> r:float -> t:float -> float

> presentValue 2000.0 0.07 10.;;

val it : float = 1016.698584

>

• Formulae can be represented very directly

Page 6: F# Presentation for SmartDevs, Hereford

The ‘map’ function

Take some collection (array, list, IEnumerable)

For every value calculate and return some other value

Result is another array/list/IEnumerable containing the results

let someSquares min max =

// This generates an array from min to max:

let numbers = [|min..max|]

Array.map (fun x -> x * x) numbers

val someSquares : min:int -> max:int -> int []

> someSquares 100 110;;

val it : int [] =

[|10000; 10201; 10404; 10609; 10816; 11025; 11236; 11449; 11664;

11881;

12100|]

>

Page 7: F# Presentation for SmartDevs, Hereford

The forward pipe operator |>

Takes the output from the preceding function

Places it into the (last) parameter of the following function

let add x y =

x + y

let multiply x y =

x * y

val add : x:int -> y:int -> int

val multiply : x:int -> y:int -> int

> add 2 3 |> multiply 5;;

val it : int = 25

>

Page 8: F# Presentation for SmartDevs, Hereford

The forward pipe operator |> (2)

Comes into its own when dealing with collections

let rootMeanSquare min max =

[|min..max|]

|> Array.map (fun x -> x * x)

|> Array.average

|> sqrt

val rootMeanSquare : min:float -> max:float -> float

> rootMeanSquare -10.0 10.0;;

val it : float = 6.055300708

>

Page 9: F# Presentation for SmartDevs, Hereford

The ‘mapi’ function

Like ‘map’ but provides you with an index value 0, 1, 2 etc.

let someAdditions min max =

[|min..max|]

|> Array.mapi (fun i x -> i + x)

val someAdditions : min:int -> max:int -> int []

> someAdditions 100 110;;

val it : int [] = [|100; 102; 104; 106; 108; 110; 112;

114; 116; 118; 120|]

>

Page 10: F# Presentation for SmartDevs, Hereford

Calculating present value of a cashflow

Calculate the present value of a cashflow starting at time 0

let presentValue fv r t = fv * (1.+r) ** (-t)

let cfPresentValue cf r =cf|> Array.mapi (fun t amt -> presentValue amt r (float(t)))|> Array.sum

val presentValue : fv:float -> r:float -> t:float -> float

val cfPresentValue : cf:float [] -> r:float -> float

> cfPresentValue [|1000.0; 2000.0; 2500.0|] 0.07;;

val it : float = 5052.755699

>

Page 11: F# Presentation for SmartDevs, Hereford

Great Circle Distance

Page 12: F# Presentation for SmartDevs, Hereford

Unit Testing and TDD

Create a new F# library project

Use Nuget to bring in FSUnit

Add GreatCircleTests.fs

module GreatCircleTests

open NUnit.Framework

open FsUnit

Page 13: F# Presentation for SmartDevs, Hereford

Write a trivial test!

module GreatCircleTests

open NUnit.Frameworkopen FsUnit

[<TestFixture>]type ``Given the GreatCircleDistance function``() =

[<Test>]member x.``The function returns 0 for a journey between the same

points``() =let expected = 0.let actual = GreatCircle.Distance 45. 50. 45. 50.actual |> should equal expected

Page 14: F# Presentation for SmartDevs, Hereford

Pass the test!

module GreatCircle

let Distance lat1 long1 lat2 long2 =0.

Page 15: F# Presentation for SmartDevs, Hereford

What’s the next simplest test?

[<Test>]member x.``The function returns 20014 km for a journey between the poles``() =

let expected = 20014.let actual = GreatCircle.Distance -90. 0. 90. 0.actual |> should equal expected

Page 16: F# Presentation for SmartDevs, Hereford

We’re gonna need an algorithm!

var R = 6371; // km

var dLat = (lat2-lat1).toRad();

var dLon = (lon2-lon1).toRad();

var lat1 = lat1.toRad();

var lat2 = lat2.toRad();

var a = Math.sin(dLat/2) * Math.sin(dLat/2) +

Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);

var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

var d = R * c;

Page 17: F# Presentation for SmartDevs, Hereford

How’s this?

open System

let Distance lat1 lon1 lat2 lon2 =

let EarthRadius = 6378.1let Deg2Rad deg =

deg * Math.PI / 180.

let lat1r = lat1 |> Deg2Radlet lat2r = lat2 |> Deg2Rad

let dLat = lat2 - lat1 |> Deg2Radlet dLon = lon2 - lon1 |> Deg2Rad

let a = (sin(dLat/2.) ** 2.) + (sin(dLon/2.) ** 2.) * cos(lat1r) * cos(lat2r)

let c = 2. * atan2 (sqrt(a)) (sqrt(1.-a))

c * EarthRadius

Page 18: F# Presentation for SmartDevs, Hereford

Whuuuuuuuuuuuuuuuuuu?

------ Test started: Assembly: FSUnitTestingExample.dll ------

Test 'GreatCircleTests+Given the GreatCircleDistance function.The

function returns 20014 km for a journey between the poles' failed:

Expected: 20014.0d

But was: 20037.392103861061d

at FsUnit.TopLevelOperators.should[a,a](FSharpFunc`2 f, a x,

Object y)

C:\Code\VS2012\FSUnitTestingExample\FSUnitTestingExample\Gre

atCircleTests.fs(28,0): at GreatCircleTests.Given the

GreatCircleDistance function.The function returns 20014 km for a

journey between the poles()

1 passed, 1 failed, 0 skipped, took 0.37 seconds (NUnit 2.6.1).

Page 19: F# Presentation for SmartDevs, Hereford

They all laughed at Christopher Columbus…

[<TestFixture>]type ``Given the GreatCircleDistance function``() =

let margin = 0.003

[<Test>]member x.``The function returns 0 for a journey between the same points``() =

let expected = 0.let actual = GreatCircle.Distance 45. 50. 45. 50.actual |> should (equalWithin margin) expected

[<Test>]member x.``The function returns 20014 km for a journey between the poles``() =

let expected = 20014.let actual = GreatCircle.Distance -90. 0. 90. 0.let error = expected * marginactual |> should (equalWithin error) expected

Page 20: F# Presentation for SmartDevs, Hereford

Further tests better added as cases

// Travel no distance:[<TestCase(0., 0., 0., 0., 0.)>]// Travel along the equator eastwards for 90 degrees:[<TestCase(0., 0., 0., 90., 10018.79)>]// Travel along the equator westwards for 90 degrees:[<TestCase(0., 0., 0., -90., 10018.79)>]// Travel along the equator eastwards for 180 degrees:[<TestCase(0., 0., 0., 180., 20037.58)>]// Travel along the equator westwards for 180 degrees:[<TestCase(0., 0., 0., -180., 20037.58)>]// Travel along the meridian northwards 90 degrees:[<TestCase(0., 0., 90., 0., 10018.79)>]// Travel along the meridian soutwards 90 degrees:[<TestCase(0., 0., -90., 0., 10018.79)>]// Travel from Farnham to Reigate:[<TestCase(51.214, -0.799, 51.230, -0.188, 42.6)>]// Travel from London to Sidney Australia:[<TestCase(51.51, -0.13, -33.86, 151.21, 16998.)>]

member t.``the function returns the right result``(lat1, long1, lat2, long2, expected) =let actual = GreatCircle.Distance lat1 long1 lat2 long2let error = expected * marginactual |> should (equalWithin error) expected

Page 21: F# Presentation for SmartDevs, Hereford

Information-Rich Programming

Bring large, structured data sources into the code in a type-safe way…

with Intellisense!

Implemented by ‘Type Providers’

Introduced with F#3.0 (VS2012)

Code-gen free!

Page 22: F# Presentation for SmartDevs, Hereford

Information Rich Programming

22

Page 23: F# Presentation for SmartDevs, Hereford

Freebase

Page 24: F# Presentation for SmartDevs, Hereford

Nuget and Fsharp.Data

Page 25: F# Presentation for SmartDevs, Hereford

Get airports and locations/// Gets airports between a specified start and end index (to facilitate paged access).

let GetAirportsPaged startIndex pageSize =

query {

for airport in dc.Transportation.Aviation.Airports do

where ( airport.Geolocation <> null

&& airport.Geolocation.Latitude.HasValue

&& airport.Geolocation.Longitude.HasValue

)

skip startIndex

take pageSize

select (airport.Name,

airport.Geolocation.Latitude.Value,

airport.Geolocation.Longitude.Value)

}

|> Array.ofSeq

|> Array.map (fun (name, lat, long) -> { Name = name; Lat = lat; Long = long })

Page 26: F# Presentation for SmartDevs, Hereford

Get airports and locations (2)

/// Gets all airports from Freebase which have a defined location.

let GetAllAirports pageSize =

let rec getPage startIndex acc =

let page = GetAirportsPaged startIndex pageSize

if page.Length > 0 then

Array.append acc (getPage (startIndex+pageSize) page)

else

acc

getPage 0 [||]

Page 27: F# Presentation for SmartDevs, Hereford

Get closest airports

/// Gets the closest n airports to the given airport.

let GetClosest target count airportList =

airportList

|> Array.map (fun airport -> airport, (DistanceBetween(airport.Lat) (airport.Long) (target.Lat) (target.Long)))

|> Array.sortBy (fun (airport, distance) -> distance)

|> Seq.truncate count

|> Array.ofSeq

Page 28: F# Presentation for SmartDevs, Hereford

Get closest airports to a named airport

/// Gets airports near an airport specified by name.

let GetAirportsNear name airportList =

let target = airportList

|> Array.tryFind (fun airport -> airport.Name.Contains(name))

if target.IsSome then

airportList

|> GetClosest target.Value 20

|> Array.iter (fun (airport, distance) -> printfn "%s - %f km" airport.Name distance)

else

printfn "Could not find %s" name