Prezentacja

advertisement
F#
Czyli funkcyjny .NET
Jakub Rusiłko
PLAN PREZENTACJI
•
Wstęp
•
Co to jest programowanie funkcyjne
•
C# vs F#
•
Cechy języka F#
•
Typy
•
Currying i Partial Function Application
•
OOP w F#
•
Asynchroniczność w F#
WSTĘP
•
Kim jestem?
•
Dlaczego F# i co fajnego jest w programowaniu funkcyjnym?
ZNIECHĘCAJĄCY KOD FUNKCYJNY
((n.lisp_token_pos_guess is to)
((year))
((p.lisp_token_pos_guess is sym)
((pp.lisp_token_pos_guess is sym)
((cardinal))
((lisp_num_digits < 4.6)((year)) ((digits))))
((lisp_num_digits < 4.8)
((name < 2880)
((name < 1633.2)
((name < 1306.4)((cardinal))((year)))
((year)))
((cardinal)))
((cardinal)))))))))
POGRAMOWANIE FUNKCYJNE – KILKA DEFINICJI
•
Programowanie funkcyjne (z wikipedii) – filozofia i metodyka programowania będąca
odmianą programowania deklaratywnego, w której funkcje należą do wartości
podstawowych, a nacisk kładzie się na wartościowanie (często rekurencyjnych) funkcji, a
nie na wykonywanie poleceń.
•
Programowanie funkcyjne jest jak opisywanie twojego zadania matematykowi.
Programowanie imperatywne jest jak wydawanie instrukcji idiocie.
•
Programowanie funkcyjne traktuje wykonanie programu jak ewaluację funkcji
matematycznej i stara się unikać stanu oraz zmiennych.
PODZIAŁ JĘZYKÓW FUNKCYJNYCH
•
języki czysto funkcyjne - nie ma zmiennych, nie ma efektów ubocznych, leniwe
wartościowanie, we/wy musi się odbywać alternatywnym sposobem, jak na przykład
monady (np. Haskell)
•
języki mieszane - można stosować zmienne, tworzyć efekty uboczne, tradycyjne we/wy,
mieszać styl funkcyjny z imperatywnym lub obiektowym, wartościowanie przeważnie
zachłanne (np. Lisp, Clojure, Scheme, Erlang, Scala, F#)
KIEDY PROGRAMOWANIE FUNKCYJNE MOŻE
OKAZAĆ CI SIĘ POMOCNE
•
Gdy masz trudności z przewidzeniem rezultatu zmian w swoim kodzie z powodu ukrytych
zależności i subtelności
•
Gdy zdajesz sobie sprawę, że ciągle tworzysz te same wzorce i szablony poświęcając
mało czasu na kluczowe i interesujące aspekty problemu
•
Masz trudności z analizą swojego kodu i martwisz się tym, czy dany fragment zostanie
wykonany we właściwej kolejności i przy odpowiednich warunkach
•
Masz trudności z wyrażaniem abstrakcji, która ukrywa JAK kod ma się wykonać, a wyraża
tylko CO chcesz osiągnąć
•
Masz problemy z ogarnięciem kontroli nad kodem asynchronicznym
•
Gdy kod zachowuje się inaczej na produkcji i inaczej podczas testów jednostkowych
F# - HISTORIA
•
Początki programowania funkcyjnego to Information Processing Language z 1956, a
potem Lisp w 1958
•
Języki funkcyjne szybko zostały wyparte przez języki imperatywne jak Fortran (1957) czy
COBOL (1959)
•
W 1973 powstaje język ML. Jest on na tyle dobry, że powstaje wiele języków pochodnych
jak Standard ML, Caml i OCaml, który łączy styl funkcyjny z obiektowo zorientowanym
stylem imperatywnym
•
W 2005 powstaje F#, który w dużej mierze jest .NETową implemantacją OCamla.
CECHY JĘZYKA F#
•
Statycznie typowany – kompilator zna typy zmiennych i funkcji w momencie kompilacji
•
Silnie typowany – zmienne nie zmieniają swojego typu
•
F# nie przeprowadza automatycznego rzutowania typów (tak jak C# czy VB), trzeba
rzutować explicite
•
Zachęca do tworzenia kodu z użyciem zmiennych niemutowalnych, ale pozwala używać
zmiennych mutowalnych, jeśli jest to konieczne
•
Pozwala na korzystanie z bibliotek napisanych w innych językach rodziny .NET i bez
problemu się z nimi łączy
•
Łączy zalety języka funkcyjnego z obiektowym
•
Zamiast nawiasów klamrowych { i } stosuje wcięcia linii
•
Wnioskowanie typów (Type Inference) – analogicznie do var w C#
CECHY JĘZYKA F#
•
Nie używamy słowa return – zwrot wartości z funkcji jest automatyczny
•
Unit zamiast void
•
Automatyczna generalizacja
•
Kolejność plików w projekcie ma znaczenie
PROSTY PROGRAM W F#
open System
let a = 2
Console.WriteLine a
PROSTY PROGRAM W C#
using System;
namespace ConsoleApplication1
{
class Program
{
static int a()
{
return 2;
}
static void Main(string[] args)
{
Console.WriteLine(a);
}
}
}
F# INTERACTIVE
•
Interaktywna konsola wspomagająca programowanie
•
DEMO
TYPY W F#
•
Typy proste (int, char, float, …)
•
Typy z bibliotek .NET
•
Typy właściwe dla F#
TUPLES (KROTKI)
1. let t1 = (2,3)
2. let t2 = ("hello",42)
3. let t3 = (42,true,"hello")
4. let z = 1,true,"hello",3.14
// "construct"
5. let z1,z2,z3,z4 = z
// "deconstruct"
6. let _,z5,_,z6 = z
7. let first = fst t1
8. let second = snd t1
// ignore 1st and 3rd elements
PROSTA ZAMIANA MIEJSCAMI W KROTCE
(TUPLE) W F# VS C#
F#
let swap (x,y) = (y,x)
C#
Tuple<U, T> Swap<T, U>(Tuple<T, U> t)
{
return new Tuple<U, T>(t.Item2, t.Item1);
}
RECORDS (REKORDY)
1. type ComplexNumber = { real: float; imaginary: float }
2. type GeoCoord = { lat: float; long: float }
3. let myGeoCoord = { lat = 1.1; long = 2.2 }
// "construct"
4. let { lat=myLat; long=myLong } = myGeoCoord
// "deconstruct”
5. let x = myGeoCoord.lat
6. let g1 = {lat=1.1; long=2.2}
7. let g2 = {g1 with lat=99.9}
// create a new one
DISCRIMINATED UNION TYPE
•
Typ będący sumą kilku typów
type IntOrBool =
| I of int
| B of bool
type Person = {first:string; last:string}
// define a record type
type IntOrBool = I of int | B of bool
type MixedType =
| Tup of int * int
// a tuple
| P of Person
// use the record type defined above
| L of int list
// a list of ints
| U of IntOrBool
// use the union type defined above
DISCRIMINATED UNION VS ENUM ORAZ PATTERN
MATCHING
•
type SizeUnion = Small | Medium | Large
// union
•
type ColorEnum = Red=0 | Yellow=1 | Blue=2
// enum
•
DEMO
NULL I OPTION TYPE
•
W czystym F# nie ma pojęcia nulla (istnieje tylko w celu kompatybilności z .net)
•
Aby oznaczyć brak wartości stosujemy Option Type
•
Podobne do Nullable w C# z tą różnicą, że Option można użyć z dowolnym typem
(również na typach referencyjnych, klasach, itp.)
type Option<'a> =
| Some of 'a
| None
•
DEMO
UNITS OF MEASURE
•
[<Measure>] type m
•
[<Measure>] type sec
•
[<Measure>] type kg
•
let distance = 1.0<m>
•
let time = 2.0<sec>
•
let speed = 2.0<m/sec>
•
let acceleration = 2.0<m/sec^2>
•
let force = 5.0<kg m/sec^2>
•
[<Measure>] type N = m/sec^2
KOLEKCJE - LISTY
let numbers = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
let numbers2 = 1 :: 2 :: 3 :: 4 :: []
let numbers3 = [1 .. 5]
let numbers4 = [1 .. 2 .. 10]
let numbers5 = List.init 10 (fun i -> i)
•
DEMO
KOLEKCJE - SEKWENCJE
•
Są podobne do list z tą różnicą, że ich wartości są wyliczane na bieżąco, gdy są
potrzebne (leniwie - LAZY)
let sequence1 = seq { 1 .. 10 }
let sequence2 = seq {10 .. -1 .. 0}
let sequence3 = seq { for a in 1 .. 10 do yield a, a*a, a*a*a }
•
DEMO
NIEZMIENNOŚĆ (IMMUTABILITY)
•
Słowo kluczowe let definiuje wartość
•
Value Binding – (wiązanie wartości) pozwala powiązać wartość z symbolem
•
Niezmienność wymusza inne spojrzenie na problemy
•
Każda kolejna operacja na zadeklarowanej wartości tworzy nową wartość (nie zmienia
starej) – analogia do typu string z C#
•
Rekurencja zamiast pętli
•
Niezmienność zachęca do używania pojedynczych wyrażeń zamiast sekwencji poleceń
sprawiając, że program jest bardziej deklaratywny
Przykład w C#:
var res = ImmutableList.Empty<int>().Add(1).Add(3).Add(5).Add(7);
//Sytem.Collections.Immutable (.NET 4.5)
FUNKCJE JAKO WARTOŚCI
•
Funkcja jest wartością i może być użyta w każdej sytuacji, w której możemy użyć
zwykłego int’a czy string’a (First-class functions), każda funkcja ma typ (w C# używamy
do tego delegatów, w F# typ jest właściwością samej funkcji)
•
W szczególności funkcja może być parametrem do innej funkcji lub wynikiem wyjściowym
funkcji – funkcje wyższego rzędu (Higher-order functions)
•
DEMO (agregacja)
SYGNATURA FUNKCJI
•
int -> int -> int
•
int -> unit
•
unit -> string
•
int -> (unit -> string)
•
'a list -> 'a
•
('a -> bool) -> 'a list -> 'a list
•
DEMO
CURRYING
•
Ale dlaczego sygnatury funkcji nie rozróżniają między parametrami a typem wyjściowym?
•
CURRYING – rozbijanie wieloargumentowych funkcji na mniejsze jedno-parametrowe
funkcje
•
Haskell Curry – matematyk, który przyczynił się do rozwoju programowania funkcyjnego
•
int -> int -> int jest tak naprawdę połączeniem więcej niż jednej funkcji
•
Nie musimy się tym martwić, kompilator robi to za nas automatycznie
•
DEMO
PARTIAL FUNCTION APPLICATION
•
Dzięki curryingowi wywołanie funkcji z mniejszą ilością parametrów, niż to wynika z
definicji funkcji, jest dozwolonym działaniem
•
Wywołanie funkcji z n-początkowymi parametrami zwróci nową funkcję przyjmującą
pozostałe (z oryginalnej funkcji) parametry
•
Właściwość ta jest jednym z najważniejszych narzędzi programowania funkcyjnego
•
DEMO
KILKA CIEKAWYCH OPERATORÓW
•
|>
- forward pipe operator – przekazuje rezultat operacji po lewej stronie do funkcji po
prawej stronie
•
<|
- backward pipe operator
•
>>
- forward composition operator - złożenie funkcji
•
<<
- backward composition operator - złożenie funkcji (w odwrotnej kolejności)
•
DEMO
OBIEKTOWY F#
•
Pozwala zaimplementować algorytmy obiektowe 1 do 1
•
Ułatwia integrację z .NETem
•
Dla początkujących może przysłonić korzyści płynące z programowania czysto
funkcyjnego
•
Nie współpracuje dobrze z funkcjami wyższego poziomu oraz z wnioskowaniem typów
•
DEMO
OBJECT EXPRESSIONS
•
Pozwala implementować interfejs w locie bez potrzeby tworzenia klasy
let makeResource name =
{ new System.IDisposable
with member this.Dispose() = printfn "%s disposed"
name }
ASYNCHRONOUS WORKFLOWS
•
DEMO
MESSAGES AND AGENTS
•
MailboxProcessor implementuje podejście bazujące na agentach i wiadomościach (kolejki
wiadomości)
•
Działa w osobnym wątku
•
Pozwala łatwo zarządzać dzielonymi zasobami bez zakleszczeń
•
Umożliwia łatwe rozdzielenie odpowiedzialności poprzez tworzenie osobnych agentów
obsługujących różne rzeczy
•
DEMO
QUICKSORT C#
public class QuickSortHelper
{
public static List<T> QuickSort<T>(List<T> values) where T : IComparable
{
if (values.Count == 0) { return new List<T>(); }
T firstElement = values[0];
var smallerElements = new List<T>();
var largerElements = new List<T>();
for (int i = 1; i < values.Count; i++)
{
var elem = values[i];
if (elem.CompareTo(firstElement) < 0) { smallerElements.Add(elem); }
else {largerElements.Add(elem);}
}
var result = new List<T>();
result.AddRange(QuickSort(smallerElements.ToList()));
result.Add(firstElement);
result.AddRange(QuickSort(largerElements.ToList()));
return result;
}
}
QUICKSORT F# - W STYLU FUNKCYJNYM
let rec quicksort list =
match list with
| [] -> []
| firstElem::otherElements ->
let smallerElements = otherElements |> List.filter (fun e -> e < firstElem) |> quicksort
let largerElements = otherElements |> List.filter (fun e -> e >= firstElem) |> quicksort
List.concat [smallerElements; [firstElem]; largerElements]
let rec quicksort2 = function
| [] -> []
| first::rest ->
let smaller,larger = List.partition ((>=) first) rest
List.concat [quicksort2 smaller; [first]; quicksort2 larger]
ŹRÓDŁA
• http://pl.wikipedia.org/wiki/Programowanie_funkcyjne
• http://fsharpforfunandprofit.com/
• http://en.wikibooks.org/wiki/F_Sharp_Programming
• Real-World Functional Programming, Tomas Petricek i Jon
Skeet, Manning Publications, 2010
KONIEC
Download