F# from the C# point of view
Michal Franc
Pragmatic Developer @ JustGiving
www.mfranc.com
@francmichal
Agenda
- Functional vs C#-like
- Lot's of samples C# vs F#
- Some F# magic
Imperative vs Functional
no implicit mutation
recurrence, functions, pattern matchin
functions, record types, union types, function compostion
mutable data
loops, functions, if statements
objects, structs
inheritance, composition, polimorphism
Functional C#
Linq - declarative + functional
Anonymous functions
lambda / delegate
type inference - var, dynamic
tuple
generics
C#
Hybrid with a more emphasis on object oriented world
F#
Hybrid with a more emphasis on functional world
Why F# ?
- immutability by default
- hybrid
- step by step functional
- .NET interop
- VS support
Blockers
syntaxplatformhard stuff- OOP experience
- addiction to mutable state
- Functional Thinking
- intuition / prior experience
- R#
Into the Functional World
C# vs F#
Demo
List counter
C# - count
var current = 0;
foreach(elem in list) {
current++;
}
List - Count
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
F# - count
Mutable state
let list = [1..100]
let count list =
let mutable counter = 0;
for i in list do
counter <- counter + 1
counter
count list
F# - count
let list = [1..10000000]
let rec count list =
match list with
| i::tail -> 1 + (count tail)
| [] -> 0
count list
Recursion!
StackOverflow!
F# - count
let list = [1..10000000]
let rec count acc list =
match list with
| i::tail -> (count (acc + 1) tail)
| [] -> acc
count 0 list
Tail Recursion
Demo
Encapsulation
C# - object
public class Human
{
public int Life { get; set; }
public Human()
{
this.Life = 100;
}
public void TakeHit(int val)
{
this.Life -= val;
}
}
F# - object
type Human() =
let mutable _life = 100
member this.takeHit value = _life <- _life - value
member this.Life
with get () = _life
let human = new Human()
type Human(life) =
let _life = life
member this.takeHit value = new Human(_life - value)
member this.Life
with get () = _life
human.takeHit 10
human.Life
let human = new Human(100)
let newHuman = human.takeHit 10
newHuman.Life
F# - record type
type Item = {
Id : int
Name : string
IsAwesome : bool
IsActive : bool
}
let createDefault = {
Id = 1
Name = "first"
IsAwesome = true
IsActive = true
}
let first = { createDefault with Name = "first" }
let second = { first with Quantity = 5 }
let third = { second with IsActive = false; IsAwesome = false }
let isAwesomeButNotActive item =
match item with
| { IsActive = false; IsAwesome = true } -> true
| _ -> false
isAwesomeButNotActive { createDefault with IsActive = false }
F# - record type
type Human = {
Life : int
}
let createNew = {
Life = 100
}
let takeHit value human = {
human with Life = human.Life - value
}
let human = createNew
let newHuman = takeHit 10 human
F# - record type
type Human = {
Life : int
} with member this.takeHit value = {
this with Life = this.Life - value
}
let createNew = {
Life = 100
}
let human = createNew
let newHuman = human.takeHit 10
C# - edge cases
public void TakeHit(int val)
{
if (val < 0) throw new ArgumentOutOfRangeException();
this.Life -= val;
if (this.Life < 0) this.Life = 0;
}
F# - edge cases
let takeHit value human =
if value < 0 then raise (System.ArgumentException())
{
human with Life = if (human.Life - value) < 0
then 0 else human.Life - value
}
let takeHit value human =
if value < 0 then invalidArg "value" "cannot be less than 0"
...
F# - edge cases
let takeHit value human =
if value < 0 then invalidArg "value" "cannot be less than 0"
{
human with Life = match (human.Life, value) with
| (l, v) when l - v < 0 -> 0
| (l, v) -> l - v
}
let takeHit value human =
if value < 0 then invalidArg "value" "cannot be less than 0"
{
human with Life = match human.Life - value with
| difference when difference < 0 -> 0
| difference -> difference
}
C#
public void Attack(Human target)
{
target.TakeHit(this.Strength);
}
public void Attack(Human target)
{
target.TakeHit(this.Strength);
this.Stamina -= 10;
if (this.Stamina < 0) this.Stamina = 0;
}
public void Attack(Human target)
{
if (this.Stamina - 10 < 0) return;
target.TakeHit(this.Strength);
this.Stamina -= 10;
if (this.Stamina < 0) this.Stamina = 0;
}
F#
let attackTarget human target =
takeHit human.Strength target
let attackTarget human target =
(
takeHit human.Strength target,
{ human with Stamina = human.Stamina - 10 }
)
let human = createNew
let target = createNew
let (newHuman, newTarget) = attackTarget human target
let difference0 one two = match one - two with
| difference when difference < 0 -> 0
| difference -> difference
let attackTarget human target =
(
takeHit human.Strength target,
{ human with Stamina = difference0 human.Stamina 10 }
)
let private (-@) one two = match one - two with
| difference when difference < 0 -> 0
| difference -> difference
let attackTarget human target =
(
takeHit human.Strength target,
{ human with Stamina = human.Stamina -@ 10 }
)
NULL in F#
*it is possible just like pointers in C#
[<AllowNullLiteral>] - interop
F# Patterns
let basic item =
printf "%i" item
let newLine item =
printf "\n%i" item
type Strategy(displayFunction) =
member this.DisplayFunction list =
List.iter displayFunction list
Strategy(basic).DisplayFunction [1..6]
Strategy(newLine).DisplayFunction [1..6]
F# Patterns
type A private () =
static let instance = A()
static member Instance = instance
member this.Action() = printfn "singleton"
A.Instance.Action()
F# Interfaces
type ICalc =
abstract member Calc: int -> int -> int
type Calculator() =
interface ICalc with
member this.Calc x y =
x + y
type ICalc =
abstract member Calc: int -> int -> int
let addCalc =
{ new ICalc with member this.Calc x y = x + y }
let calc = addCalc
calc.Calc 1 1
let calc = new Calculator() :> ICalc
calc.Calc 1 1
F# Interfaces
type CalcService = {
Add : int -> int -> int
Substract : int -> int -> int
Multiply : int -> int -> int
Divide : int -> int -> int
}
let service = {
Add = fun x y -> x + y
Substract = fun x y -> x - y
Multiply = fun x y -> x * y
Divide = fun x y -> x / y
}
C# Interfaces
public class ImageRepo
{
private IImageProvider imageProvider;
public ImageRepo(IImageProvider imageProvider)
{
this.imageProvider = imageProvider;
}
public byte[] GetImage(string imageName)
{
return this.imageProvider.GetImage(imageName);
}
}
var fromDB = new ImageRepo(new IMageFromDataBaseProvider());
var fromUrl = new ImageRepo(new IMageFromUrlProvider());
var fromSrv = new ImageRepo(new IMageFromFileProvider());
F# Interfaces
type ImageFrom = URL | SRV | DB
let imageRepoBuilder imageFrom =
match imageFrom with
| URL -> imageRepo imageFromUrl
| SRV -> imageRepo imageFromSrv
| DB -> imageRepo imageFromDb
let imageFrom imageType name =
imageRepoBuilder imageType name
imageFrom URL "test.jpg"
imageFrom DB "test.jpg"
imageFrom SRV "test.jpg"
http://i.imgur.com/FLf5J6f.jpg
QA
More
http://fsharpforfunandprofit.com/
http://fsharpforfunandprofit.com/rop/
http://www.tryfsharp.org/
http://www.amazon.com/Real-World-Functional-Programming-With-Examples/dp/1933988924
http://fsharp.org/
https://sergeytihon.wordpress.com/category/f-weekly/
http://www.cs.uni.edu/~wallingf/blog-images/computing/make-let-not-var-small.jpg
End
Michal Franc
www.mfranc.com
@francmichal
Fnctional Way - C# to F#
By Michal Franc
Fnctional Way - C# to F#
From OOP to Functional Programming [ENG]
- 1,713