From OOP to Functional Programming
czyli F# oczami programisty C#
Michal Franc
Pragmatic Developer @ JustGiving
www.mfranc.com
@francmichal
- PWR
- 4 lata Wrocek, 1 rok Londyn @JustGiving
- 90% backend / 10% frontend
- 70% dev / 30 % lead
- self-organizing teams believer
- Początkujący speaker i Fsharper
- bloger - www.mfranc.com
- dotnetconf.pl
- Vim lover
O mnie
Agenda
- Paradygmaty programowania
- Funkcjonalne vs C#-powo
- Sample C# vs F#
- F# magic
Imperatywnie
Instrukcja po instrukcji
mutacja danych
C, C++
Nacisk na polecenia dla komputera
Obiektowo
rozwinięcie imperatywnego podejścia
dodanie konstrukcji pozwalających
modelować kod za pomocą obiektów
C++, Java, C#
renesans dzięki DDD
Funkcjonalnie
bez mutacji danych
Funkcje zwracające funkcje przyjmujące funkcje
Scala, Haskell, Clojure, F#
Nacisk na funkcje
Deklaratywnie
Dla danego wejścia opisanie żądanego wyjścia
SQL
Manipulacje na zbiorze danych
Imperatywnie vs Funckjonalnie
(obiektowo)
brak mutacji danych
rekurencja, funkcje, pattern matching
funkcje, record types, union types
kompozycja funkcji
mutacje danych
pętle, funkcje, ify
obiekty, struktury
dziedziczenie, kompozycja, polimorfizm obiektów
Funckjonalny C#
Linq - deklaratywnie + funkcjonalnie
Anonimowe funkcje
wyrażenia lambda / delegate
type inference - var, dynamic
tuple
C#
Hybryda z naciskiem na programowanie obiektowe
F#
Hybryda z naciskiem na programowanie funkcjonalne
Dlaczego F# ?
- immutability by default
- hybrydowy język
- Nie trzeba rzucać się na głęboką wodę
- małymi krokami funkcjonalnie
- .NET interop
- wsparcie VS ( w pewnym zakresie )
Blokery
skladniaplatformanowe trudne pojecia- OOP experience
- przyzwyczajenie do mutacji danych
- Functional Thinking
- intuicja / doświadczenie
- R#
Funkcjonalne Transformacje
C# vs F#
Demo
Licznik lista
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
Mutacja Danych
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
Rekurencja!
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
Enkapsulacja
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#
*da się poszaleć z nullem tak jak da się włączyć wskaźniki w 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"
Czy warto ?
- Dojrzałe community
- inne podejście
- nowe rozwiązania
- szersze spojrzenie
- większa wiedza
- Scala, Clojure, Haskell - przystępniejszy
- lepsze niż kolejny framework
- blows your mind
Komercyjnie ?
- rozkręca sie
- specjalizacja - koder + data science, math, domain
- CS we Wrocku
- W Londku sporo ofert -http://functionalworks.ghost.io/ 150% mediania vs C#
- w JG próbujemy
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
From OOP to Functional Programming [PL]
By Michal Franc
From OOP to Functional Programming [PL]
From OOP to Functional Programming
- 2,451