1 Czyli funkcyjny .NET Jakub Rusiłko
2 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#
3 Wstęp Kim jestem? Dlaczego F# i co fajnego jest w programowaniu funkcyjnym?
4 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 < ) ((name < )((cardinal))((year))) ((year))) ((cardinal))) ((cardinal)))))))))
5 Pogramowanie funkcyjne – kilka definicjiProgramowanie 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.
6 Podział języków funkcyjnychję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#)
7 Kiedy programowanie funkcyjne może okazać ci się pomocneGdy 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
8 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.
9 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#
10 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
11 Prosty program w F# open System let a = 2 Console.WriteLine a Zmienne są niezmienne więc nazywane są raczej wartościami.
12 Prosty program w C# using System; namespace ConsoleApplication1 { class Program static int a() return 2; } static void Main(string[] args) Console.WriteLine(a);
13 F# Interactive Interaktywna konsola wspomagająca programowanie DEMO
14 Typy w F# Typy proste (int, char, float, …) Typy z bibliotek .NETTypy właściwe dla F#
15 Tuples (Krotki) let t1 = (2,3) let t2 = ("hello",42)let t3 = (42,true,"hello") let z = 1,true,"hello", // "construct" let z1,z2,z3,z4 = z // "deconstruct" let _,z5,_,z6 = z // ignore 1st and 3rd elements let first = fst t1 let second = snd t1
16 Prosta zamiana miejscami w Krotce (Tuple) w F# vs C#let swap (x,y) = (y,x) C# Tuple Swap
17 Records (rekordy) type ComplexNumber = { real: float; imaginary: float } type GeoCoord = { lat: float; long: float } let myGeoCoord = { lat = 1.1; long = 2.2 } // "construct" let { lat=myLat; long=myLong } = myGeoCoord // "deconstruct” let x = myGeoCoord.lat let g1 = {lat=1.1; long=2.2} let g2 = {g1 with lat=99.9} // create a new one
18 Discriminated Union TypeTyp 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
19 Discriminated Union vs enum oraz Pattern Matchingtype SizeUnion = Small | Medium | Large // union type ColorEnum = Red=0 | Yellow=1 | Blue= // enum DEMO
20 Null i Option type type Option<'a> = | Some of 'a | NoneW 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
21 Units of measure [
22 Kolekcje - Listy let numbers = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]let numbers5 = List.init 10 (fun i -> i) DEMO
23 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 { } let sequence2 = seq { } let sequence3 = seq { for a in do yield a, a*a, a*a*a } DEMO
24 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
25 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)
26 Sygnatura FUnkcji int -> int -> int int -> unitunit -> string int -> (unit -> string) 'a list -> 'a ('a -> bool) -> 'a list -> 'a list DEMO
27 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
28 Partial function applicationDzię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
29 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
30 Obiektowy F# Pozwala zaimplementować algorytmy obiektowe 1 do 1Uł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
31 Object expressions let makeResource name = { new System.IDisposablePozwala implementować interfejs w locie bez potrzeby tworzenia klasy let makeResource name = { new System.IDisposable with member this.Dispose() = printfn "%s disposed" name }
32 Asynchronous WorkflowsDEMO
33 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
34 Quicksort C# public class QuickSortHelper { public static List
35 Quicksort F# - w stylu funkcyjnymlet 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]
36 Źródła http://pl.wikipedia.org/wiki/Programowanie_funkcyjne Real-World Functional Programming, Tomas Petricek i Jon Skeet, Manning Publications, 2010
37 KONIEC