czyli F# oczami programisty C#
Michal Franc
Pragmatic Developer @ JustGiving
www.mfranc.com
@francmichal
Instrukcja po instrukcji
mutacja danych
C, C++
Nacisk na polecenia dla komputera
rozwinięcie imperatywnego podejścia
dodanie konstrukcji pozwalających
modelować kod za pomocą obiektów
C++, Java, C#
renesans dzięki DDD
bez mutacji danych
Funkcje zwracające funkcje przyjmujące funkcje
Scala, Haskell, Clojure, F#
Nacisk na funkcje
Dla danego wejścia opisanie żądanego wyjścia
SQL
Manipulacje na zbiorze danych
(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
Linq - deklaratywnie + funkcjonalnie
Anonimowe funkcje
wyrażenia lambda / delegate
type inference - var, dynamic
tuple
Hybryda z naciskiem na programowanie obiektowe
Hybryda z naciskiem na programowanie funkcjonalne
C# vs F#
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++;
}
Mutacja Danych
let list = [1..100]
let count list =
let mutable counter = 0;
for i in list do
counter <- counter + 1
counter
count list
let list = [1..10000000]
let rec count list =
match list with
| i::tail -> 1 + (count tail)
| [] -> 0
count list
Rekurencja!
StackOverflow!
let list = [1..10000000]
let rec count acc list =
match list with
| i::tail -> (count (acc + 1) tail)
| [] -> acc
count 0 list
Tail Recursion
public class Human
{
public int Life { get; set; }
public Human()
{
this.Life = 100;
}
public void TakeHit(int val)
{
this.Life -= val;
}
}
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
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 }
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
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
public void TakeHit(int val)
{
if (val < 0) throw new ArgumentOutOfRangeException();
this.Life -= val;
if (this.Life < 0) this.Life = 0;
}
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"
...
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
}
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;
}
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 }
)
*da się poszaleć z nullem tak jak da się włączyć wskaźniki w C#
[<AllowNullLiteral>] - interop
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]
type A private () =
static let instance = A()
static member Instance = instance
member this.Action() = printfn "singleton"
A.Instance.Action()
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
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
}
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());
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
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
Michal Franc
www.mfranc.com
@francmichal