What is F#?
- A multi-paradigm language
(functional, imperative, object oriented) - "functional first"
A brief history
- Started out in the early 2000s as an OCaml for .NET
- With the .NET Core Framework (~2016) cross platform use becomes much move viable
- From 2015 Fable compiler (F# compiler backend that generates Javascript)
Values
and type inferrence
module Demo
let aBoolean = true
let someInt = 5
let someFloat = 4.3
let someString = "This is a string value"
let ( someOtherInt : int ) = 42
Comparing syntax
(Javascript)
function addNumbers(first, second) {
return first + second;
}
addNumbers(3, 4);
Comparing syntax
(F#)
let addNumbers first second =
first + second
addNumbers 3 4
Comparing syntax
(F#)
let addNumbers first second =
first + second
addNumbers 3 4
Currying &
partial application
module Demo
let addNumbers first second =
first + second
let addFive =
addNumbers 5
addFive 10
// 15
Calling framework functions and using classes
module Demo
let result = System.Math.Sin 1.0
open System.Math
let result2 = Sin 1.0
let randomGenerator = System.Random()
let randomValue = randomGenerator.Next()
Tuples
module Demo
let tupleValue = (5, "a string")
let printTuple (tuplevalue : int * string) =
let (firstPart, secondPart) = tupleValue
printfn "The number is %d, the string is '%s'" firstPart secondPart
printTuple tuple
Tuples
module Demo
let tupleValue = (5, "a string")
let printTuple (firstPart : int, secondPart : string) =
printfn "The number is %d, the string is '%s'" firstPart secondPart
printTuple tuple
Records
module Demo
type Person =
{ FirstName : string
LastName : string
YearOfBirth : int }
let batman =
{ FirstName = "Bruce"
LastName = "Wayne"
YearOfBirth = 1939
}
let printPerson (person : Person) =
printfn "%s %s (Born %d)" person.FirstName person.LastName
person.YearOfBirth
printPerson batman
// Bruce Wayne (Born 1939)
Records
module Demo
let batman =
{ FirstName = "Bruce"
LastName = "Wayne"
YearOfBirth = 1939
}
let theDarkKnight =
{ batman with YearOfBirth = 1986 }
printPerson batman
// Bruce Wayne (Born 1939)
printPerson theDarkKnight
// Bruce Wayne (Born 1986)
Discriminated Unions
(Sum types)
module Demo
type Direction =
| Up
| Right
| Left
| Down
let printDirection (dir : Direction) =
match dir with
| Up -> printfn "Up"
| Right -> printfn "Right"
| Left -> printfn "Left"
| Down -> printfn "Down"
printDirection Up
Discriminated Unions
(Sum types)
module Demo
type Status<'successType> =
| Pending
| Running of current : int * total : int
| Success of 'successType
| Error of string
let printStatus status =
match status with
| Pending -> printfn "Pending"
| Running(0, n) -> printfn "Starting with first item of %d" n
| Running(i, n) -> printfn "Processing %d of %d" i n
| Success success ->
printfn "Succesfully completed, result is %O" success
| Error msg -> printfn "Error: %s" msg
printStatus (Success 42)
// Succesfully completed, result is 42
printStatus (Error "unknown problem")
// Error: unknown problem
let printStatus status =
match status with
| Pending -> printfn "Pending"
| Running(0, n) -> printfn "Starting with first item of %d" n
| Running(i, n) -> printfn "Processing %d of %d" i n
| Success success ->
printfn "Succesfully completed, result is %O" success
| Error msg -> printfn "Error: %s" msg
printStatus (Success 42)
// Succesfully completed, result is 42
printStatus (Error "unknown problem")
// Error: unknown problem
printStatus (Success 42)
// Succesfully completed, result is 42
printStatus (Error "unknown problem")
// Error: unknown problem
Handling optional values
Option
module Demo
// Defined in the standard library
type Option<'a> =
| None
| Some of 'a
let doSomething (optionalInteger : Option<int>) =
match optionalInteger with
| Some value -> printfn "Got %d" value
| None -> printfn "Didn't have a value!"
Handling optional values
null
module Demo
let doSomething (person: Person) =
match person with
| null -> printfn "Didn't have a value!"
| personValue -> printfn "Got %O" personValue
Handling Errors
Result
module Demo
type Result<'success, 'error> =
| Ok of 'success
| Error of 'error
let doSomething (intResult : Result<int, string>) =
match intResult with
| Ok value -> printfn "Got %d" value
| Error err -> printfn "Got an error: %s" err
Handling Errors
Exceptions
module Demo
try
someExceptionThrowingFunction()
with
| :? IOException as ex -> printfn "IOException was thrown: %O!" ex
| _ -> printfn "Some other exception was thrown!"
Mutation
module Demo
use processInstance = new Process()
processInstance.StartInfo.FileName <- "echo"
processInstance.StartInfo.CreateNoWindow <- true
processInstance.Start();
Many options are annoying
module Demo
let createPerson (context : HttpContext) : Option<Person> =
let firstNameOption = context.TryGetQueryParam "firstname"
let lastNameOption = context.TryGetQueryParam "lastname"
let yearBornOption = context.TryGetQueryParam "yearBorn"
match firstNameOption, lastNameOption, yearBornOption with
| Some firstName, Some lastName, Some yearBorn ->
Some { FirstName = firstName
LastName = lastName
YearOfBirth = yearBorn }
| _ -> None
Computation Expressions
module Demo
let createPerson (context : HttpContext) : Option<Person> =
option {
let! firstNameOption = context.TryGetQueryParam "firstname"
let! lastNameOption = context.TryGetQueryParam "lastname"
let! yearBornOption = context.TryGetQueryParam "yearBorn"
return { FirstName = firstName
LastName = lastName
YearOfBirth = yearBorn }
}
Computation Expressions
module Demo
let retrieveFromRemoteStore key : Async<string> =
// ommited
let createPerson (context : HttpContext) : Async<Person> =
async {
let! firstName = retrieveFromRemoteStore "firstname"
let! lastName = retrieveFromRemoteStore "lastname"
let! yearBorn = retrieveFromRemoteStore "yearBorn"
return { FirstName = firstName
LastName = lastName
YearOfBirth = yearBorn }
}
Units of Measure
module Demo
[<Measure>] type m
[<Measure>] type sec
[<Measure>] type kg
let distance = 1.0<m>
let time = 2.0<sec>
let speed = distance/time
let acceleration = speed/time
let mass = 5.0<kg>
let force = mass * speed/time
Type Providers
module Demo
let msft = Stocks.Load("http://www.google.com/finance/historical?q=MSFT&output=csv")
// Look at the most recent row. Note the 'Date' property
// is of type 'DateTime' and 'Open' has a type 'decimal'
let firstRow = msft.Rows |> Seq.head
let lastDate = firstRow.Date
let lastOpen = firstRow.Open
// Print the prices in the HLOC format
for row in msft.Rows do
printfn "HLOC: (%A, %A, %A, %A)" row.High row.Low row.Open row.Close
A year of using F#
The positives
- Same language on Frontend, Backend and even in make files is great
- Algebraic Data Types are great
- Big ecosystems (.NET and Javascript) are very useful
- Asp.Net core is fast and works nicely with linux docker containers
A year of using F#
The negatives
- Documentation of F#/Fable libraries sometimes lacking
- Some pain points around library packaging if code should be used on Backend and Frontend
Where to find more
Intro to F#
By Daniel Bachler
Intro to F#
- 1,118