sobota, 30 stycznia 2016

Programowanie funkcyjne z wykorzystaniem języka F#

Pierwszy raz styczność z językiem F# miałem około półtora roku temu, kiedy to Michał Łusiak zaprezentował nam ten język podczas spotkania WG.NET. To był mój pierwszy kontakt z tym językiem. Pewne elementy wydawały się być całkiem fajne, inne nieco dziwne, ale ogólne wrażenie nt. tego języka jakie odniosłem po tej prezentacji było takie, że Microsoft traktuje ten język jako swego rodzaju "piaskownicę" oraz "miejsce treningowe" podczas wprowadzania rozszerzeń do języka C#. Że to jednak C# ma pozostać głównym językiem, bądź co bądź korporacyjnej platformy, natomiast F# to ma być takie laboratorium stworzone dla tzw. "szalonych naukowców" oraz innych eksperymentatorów, którzy będą tworzyć nowe rozszerzenia języka oraz platformy. Jeżeli stworzą coś nowego i uda im się to skompilować do MSIL, a przy okazji nic nie wybuchnie, to wtedy wszelkie nowości będzie można zaadaptować do C#.

W międzyczasie, możliwe że nawet wcześniej, podczas jednego ze szkoleń nt. C# prowadzonego przez Comarch, autor szkolenia wyjawił, że osobiście jest fanem języków funkcyjnych, oraz że obecnie, bardzo dużo, albo niemal wszystko co znamy z języków funkcyjnych da się zrobić w Linq, tyle tylko, że zapis jest wtedy nieco dłuższy i bardziej pokręcony.

Wracając jednak do właściwego F#, kolejnym takim momentem, przemawiającym na jego korzyść był fakt, iż podczas konferencji Wroc# 2015 ten język, obok Angular.JS dostał jedną z sesji prezentacyjnych (wszystkich sesji było 6). To oznaczało, że coś się dzieje.

Prawdziwym przełomem w moim postrzeganiu tego języka, była jednak konferencja devday 2015, podczas której nie tylko były sesje z jezyka F#, a na korytarzach można było osobiście porozmawiać z samym Tomasem Petrickiem, który nomenomen zrobił z jednego z niewielu dostepnych stolików swoisty "F# Corner" ;-) Prawdziwym przełomowym momentem, to był fragment prezentacji Chada Fawlera, która stanowiła keynote tej konferencji:

Chad mówi tam m.in., aby w obecnych czasach zainteresować się 3 tematami:
- linux (z którego aktualnie tworze tego posta)
- mobile development (jesienią tego roku przerobiłem kurs na courserze z programowania na platformę Android)
- programowanie funkcyjne, o którym tutaj właśnie się rozpisuje.

Więc czym tak naprawdę jest język F# i jak się do niego zabrałem?
F# jest to język o funkcyjnym paradygmacie programowania, w odróżnieniu np. od C#, Javy, Pythona lub C++, które są językami o obiektowym paradygmacie programowania. F# jest też integralnym językiem platformy .NET, co oznacza, że jego kod źródłowy kompilowany jest do języka MSIL, a dopiero później przerabiany na kod maszynowy. Język ten ma też kilka innych ciekawostek, które postaram się wylistować:
  • język funkcyjny (funkcyjny paradygmat programowania)
  • język kodu pośredniego platformy .NET
  • dopuszcza mix z kodem źródłowym innego języka .NET (przez co dopuszczalne jest korzystanie z bibliotek standardowych platformy .NET lub tworzenie projketów będących swoistymi mieszańcami C# oraz F#).
  • brak dynamicznego mapowania typów, co czasami bywa irytujące
  • wszystkie zmienne z definicji są stałe i niezmienne, ale możliwe jest tworzenie klasycznych zmiennych (poprzed dodanie słowa kluczowego "mutable"). Ma to na celu tworzenie ułatwienie rozwiązań pracujących w trybie zrównoleglonym (wielowątkowym)
  • "model aktorów", zwany tutaj "mailbox agent", znany m.in. z biblioteki "Akka" oraz "Akka.net" jest tutaj wbudowany jako naturalny element składowy jezyka 
  • open source - język jest w pełni otwarty i oficjalnie tworzony przez społeczność, choć część jego twórców dostaje za pracę nad projektem wypłatę z Microsoftu
  • hierarchia plików w solucji ma znaczenie (w odróżnieniu np. od C#, gdzie nie ma to znaczenia)
To co wypunktowałem, to są główne, charakterystyczne cechy tego języka, ale co one oznaczają w praktyce? Czym np. jest ten funkcyjny paradygmat programowania?
Funkcyjny paradygmat programowania, to w porównaniu do obiektowego paradygmatu programowania nieco inne spojrzenie na te same problemy.
   W przypadku programowania obiektowego, tworzymy klasy, jako definicję obiektów, w oparciu o które tworzymy obiekty. W tych obiektach "zamykamy" pewne dane, a nast. wykonujemy na nich pewne obiczenia. Mamy więc zbiory obiektów, każdy z nich wewnątrz ma swoje specyficzne dane, na których wykonuje obliczenia, często w sposób mniej lub bardziej tajny (tzw. enkapsulacja), a nast. operujemy na danych wynikowych wychodzących z tych obiektów.
   W przypadku programowania funkcyjnego mamy do czynienia z innym podejściem. Tutaj nie mamy typowych klas przedstawiających obiekty. Mamy za to funkcje, które przyjmują pewne wartości i które zwracają wyniki. Do tego mamy kilka typów kolekcji i... operujemy na danych z tych kolekcji za pomocą tworzonych funkcji. Jeżeli trzeba, to w naszych funkcjach, wykorzystujemy inne funkcje, w których możemy wykorzystywać kolejne funkcje itd. Oczywiście istnieje kilka typów kolekcji, różniących się od siebie właściwościami (np. sekwencje z definicji obsługują "lazy loading", natomiast Sety bazują na drzewie binarnym), jak i kilka typów kluczowych (np. rec sprawiających, że dana funkcja jest funkcją rekurencyjną).

Skąd czerpałem wiedzę?
Postanowiłem połączyć teorię z praktyką czyli:
  • Jako teorię czytałem książkę "F# for Quantitative Finance (Johan Astborg)", która ma raczej średnie opinie w necie, ale z uwagi, że wpadła w moje ręce za darmo podczas jednej z promocji na "Pact Publishing" to postanowiłem ja przeczytać i czegoś się z niej nauczyć.
  • Jako praktykę, postanowiłem wykonać przynajmniej 10 zadań z Project Euler. Jak mi poszło, można sprawdzić na w odp. projekcie, na moim koncie na githubie.
  • Połączenie jednego z drugim, czyli forum stackoverflow

Po tym nieco przydługim wstępnie, teraz czas na to, co tygryski lubią najbardziej, czyli konkluzje, czyli wnioski.
  • czytając opis języka w książce myślałem sobie, niby spoko
  • do czasu, aż zacząłem to programować samemu, wtedy co rusz napotykałem na dziwne błędy oraz zmuszenie mnie do zmiany sposobu myślenia
  • o ile pierwsze zadania z projektu euler były stosunkowo proste (przydała się praktyka w stos. linq oraz przykłady z książki, to im dalej w las tym trudniej)
  • sporo problemów i frustracji, szczególnie podczas pracy z pętlami przytrafił mi typ danych unit. Ponieważ jest on bardzo podobny do uint, na początku te typy mi się myliły, szczególnie, że F# nie pozwala na dynamiczną zmianę typów. Tworzyłem więc pętle, które z jakiegoś powodu w którymś momencie zwracały unit (brak wartości), natomiast mi się wydawało, że wraz z którąć iterecją, z nieznanego mi powodu zwracają one typ uint i mam problem z mapowaniem i porównywaniem typów. 
  • pętle w F# nie są typowymi "pętlami" tylko "wyrażeniami" lub "wyrażeniami wyższego rzędu", co oznacza, że nie posiadają typowego "break-a". Nie możemy w dowolnym momencie, ot tak sobie wyjść z pętli przerywając jej działanie, tylko musimy ją ładnie "zamknąć", tudzieź pozwolić aby wszystko się wykonało tak jak należy
  • sposobem, aby pętle nie trwały nieskończenie wiele, albo nie robiły zbyt dużo "pustych przelotów", jest zastosowanie sekwencji z ich "lazy loading", oraz słowa kluczowego "yield". Bardzo ładnie obrazuje to przykład rozwiązania problemu nr.9 https://github.com/zchpit/ProjectEuler/blob/master/F%23/ProjectEulerInF%23/ProjectEulerInF%23/Problem9.fs
  • kod napisany w F# zazwyczaj jest krótszy i bardziej "elegancki" niż w porównaniu do tradycjnej wersji C#
  • kod przynajmniej w teorii dużo łatwiej powinno się dać skalować (wielowątkowość)
  • choć nie wiem, jak wygląda kwestia jego wydajności względem C#
  • Chad Fowler zachęcał do nauki języków funkcyjnych, widząc w tym przyszłość
  • jednak ilość obecnych ogłoszeń o pracę na popularnych portalach z pracą (w PL) na chwilę obecną jakoś specjalnie do tego nie zachęca
  •  rozmawiając nt. temat z managerem jednej z firm deweloperskich, usłyszałem odp., że na razie to jest raczej nowość i nie ma na rynku zbyt dużo os., więc ewentualna zasępowalność jest niewielka i co za tym idzie na chwilę obecna bardziej ceniona jest dla nich chęć uniknięcia tzw. "Bus factor" niż "innowacyjność".
  • z drugiej strony, to natywne wsparcie dla programowania rozproszonego oraz wielowątkowego kusi, oj kusi :D

Reasumując, temat warty uwagi śledzenia. O ile obecnie jest zauważalny "hype" na konferencjach oraz grupach programistycznych, jednak jak na razie nie przekłada się to na ilość ofert pracy dla ludzi umiejących F#. W sumie najbardziej popularnym obecnie jezykiem funkcyjnym jest w polsce Scala, jednak tak naprawdę, ten język to takie "niewiadomo co", a z całą pewnością nie można go nazwać językiem typowo funkcyjnym. Z drugiej strony, od czasu rozpropagowania modelu aktorów do języków obiektowych, częściowo otwarta została pewna "nisza" wykorzystywana wcześniej jedynie przez języki funkcyjne.

Jak to się wszystko potoczy? Myślę, że bardzo dużo zależy od tego, czy będą się pojawiały ogłoszenia o pracę w tym konkretnym języku, zarówno pod względem ich ilości jak i jakości. W każdym bądź razie zamierzam temat bacznie obserwować, przynajmniej jeszcze przez jakiś czas :-)


Brak komentarzy:

Prześlij komentarz