Haskell : Język programowania funkcyjnego

edytuj

Haskell jest nowoczesnym językiem funkcyjnym ogólnego przeznaczenia stworzonym po to, aby połączyć wszystkie atuty programowania funkcyjnego w jednym eleganckim, silnym i ogólnie dostępnym języku programowania.

Czystość

edytuj

Haskell, w przeciwieństwie do niektórych języków funkcyjnych, jest językiem czysto funkcyjnym. Najważniejszą cechą tego języka jest to, iż nie pozwala na żadne efekty uboczne.

Leniwe wartościowanie

edytuj

Inną ważną cechą języka Haskell jest to, iż jest to język „leniwy” („not-strict”). Oznacza to, że wartość żadnego wyrażenia nie jest wyznaczana dopóki nie jest potrzebna. Można, na przykład, zdefiniować nieskończenie długą listę liczb pierwszych tworzoną w niekończącej się rekurencji. Wyznaczone zostaną tylko te elementy listy, które są aktualnie potrzebne. Pozwala to na wiele bardzo eleganckich rozwiązań wielu problemów. Przykładem może być zdefiniowanie listy możliwych rozwiązań oraz przefiltrowanie jej, usuwając rozwiązania niedopuszczalne. Pozostała lista będzie więc zawierała rozwiązania dopuszczalne. Leniwe wartościowanie czyni tę operację bardzo przejrzystą. Jeśli potrzebujemy tylko pierwsze rozwiązanie możemy odczytać pierwszy element z listy – leniwe wartościowanie zagwarantuje nam, że nic nie zostanie policzone niepotrzebnie.

Silne typowanie

edytuj

Haskell jest językiem stosującym silne typowanie. Oznacza to, że niemożliwa jest na przykład przypadkowa konwersja Double do Int. Prowadzi to do zmniejszenia liczby powstałych błędów. Czasami może to być nieco kłopotliwe, ale nie zdarza się to na tyle często, żeby można było uznać to za poważną niedogodność. W praktyce często pomaga to w zlokalizowaniu błędów w kodzie. W językach, które pozwalają na taką niejawną konwersję, często pojawiają się problemy, kiedy kompilator potraktuje Double jako Int, wskutek czego na przykład wynikiem dzielenia 1/2 jest liczba 0.

W przeciwieństwie do wielu języków stosujących silne typowanie, typy w Haskellu są automatycznie rozpoznawane. Oznacza to, że bardzo rzadko konieczne jest deklarowanie typu funkcji. Często takie definicje służą jedynie dokumentacji kodu. Haskell stara się wywnioskować typ z kontekstu w jakim zmienna została użyta. Następnie sprawdzana jest zgodność typów we wszystkich operacjach, aby wykluczyć niedopasowania typów.

Język Python stosuje zasadę "duck typing"; oznacza to „jeśli chodzi i mówi jak kaczka, oznacza to, że jest kaczką”. Haskell posiada nieco inny mechanizm. Jeśli jakaś wartość „chodzi i mówi jak kaczka”, wtedy zostanie potraktowana jak kaczka, ale jeśli później zacznie się zachowywać jak małpa, już podczas kompilacji zostanie zgłoszony błąd.

Haskell i błędy

edytuj

Programy napisane w Haskellu zawierają mniej błędów, ponieważ Haskell:

  • jest językiem czystym – nie ma efektów ubocznych – za każdym razem, gdy wywołamy funkcję z takimi samymi parametrami, zwróci ona taką samą wartość;
  • stosuje silne typowanie – nie ma niejawnych przekształceń typów;
  • jest zwięzły – programy są krótsze, dzięki czemu łatwiej jest analizować poszczególne funkcje i lokalizować błędy;
  • jest językiem wysokiego poziomu – programy napisane w Haskellu często są bardzo podobne do opisu algorytmu. Pisząc funkcje na wyższym poziomie abstrakcji, zostawiając szczegóły kompilatorowi, zmniejszamy szanse pojawienia się błędów;
  • zarządza pamięcią – programista nie musi się martwić o zwisające wskaźniki, odśmiecanie itp. – kompilator wykonuje to za niego; programista zajmuje się jedynie implementacją algorytmu, a nie zarządzaniem pamięcią;
  • jest modularny – Haskell oferuje bardzo wiele metod łączenia modułów. Jeśli dwie proste funkcje działają poprawnie i połączymy je w odpowiedni sposób w bardziej rozbudowaną funkcję, mamy pewność, że nowo powstała funkcja również działa poprawnie;

Haskell vs OOP (Programowanie obiektowe)

edytuj

Język Haskell, podobnie jak języki obiektowe, umożliwia tworzenie abstrakcyjnych struktur danych, polimorfizm i enkapsulację.

Enkapsulacja danych jest realizowana poprzez umieszczenie każdego typu danych w osobnym module, z którego eksportowany jest tylko interfejs i tylko ten interfejs jest widziany na zewnątrz modułu. Dzięki temu, że tylko funkcje będące częścią interfejsu mogą być użyte „na zewnątrz” – szczegóły implementacji mogą zostać ukryte wewnątrz modułu. W Haskellu typ danych oraz funkcje działające na tych danych nie są zgrupowane wewnątrz „obiektu”, ale wewnątrz modułu.

Polimorfizm jest realizowany poprzez zastosowanie tzw. klas typów. Klasy typów są w Haskellu dokładnie tym, czego można się spodziewać po ich nazwie. Są zbiorem zasad, które musi spełnić typ danych, aby mógł być traktowany jako instancja tej klasy. Można zdefiniować typ „Pies” będący instancją klasy typów „Zwierzę”. Wszystkie funkcje, które mogą zostać zastosowane w stosunku do zmiennej typu „Zwierze”, równie dobrze mogą zostać zastosowane do zmiennej typu „Pies”.

Haskell umożliwia więc polimorfizm oraz enkapsulację. Nie pozwala jednak na grupowanie danych i funkcji na nich działających w pojedynczy obiekt. Należy również pamiętać, że funkcje mogą być częścią typu danych - funkcje w Haskellu są przecież danymi tak samo jak zmienne typu Int czy String!

Szybkość języka Haskell

edytuj

Program napisany w języku Haskell i skompilowany przy użyciu GHC wykonuje się z prędkością porównywalną do C czy C++. Różnice w szybkości są jednak tak małe, że prawie bez znaczenia. Oczywiście nie dotyczy to aplikacji takich jak np. kodery MPEG, czy inne aplikacje wykonujące złożone obliczenia, które większość czasu wykonania spędzają na wykonywaniu małej części kodu. W takich przypadkach zdecydowanie lepszym rozwiązaniem jest zastosowanie języka C++.

Zgodnie z jedną z wersji zasady „80/20”, przeciętna aplikacja poświęca 80% czasu na wykonanie 20% kodu. Oznacza to, że duża część funkcji systemu jest tak mało istotna, że optymalizacja ich nie ma sensu. Może się okazać, że tylko kilka funkcji jest na tyle ważnych, że warto poświęcić czas na ich udoskonalanie. Te funkcje mogą zostać napisane np. w języku C/C++ oraz wykorzystane w Haskellu za pomocą odpowiedniego interfejsu. Czasami warto zrezygnować z szybkości działania aplikacji na rzecz produktywności, stabilności i łatwości utrzymania kodu. Czas pracy programisty jest przecież kosztowniejszy niż czas pracy procesora.

Należy również pamiętać, że optymalizacja algorytmu może dać lepsze rezultaty niż optymalizacja kodu. Jeśli aplikacja w Haskellu zostanie napisana w znacznie krótszym czasie niż w języku C++, pozostanie więcej czasu na pracę nad poprawą samego algorytmu.

Dlaczego Haskell nie jest popularny?

edytuj

Większość osób zaczyna programowanie od jednego z języków imperatywnych. Przejście z jednego języka imperatywnego do innego nie stanowi większego problemu. Programowanie w języku funkcyjnym, a tym bardziej czysto funkcyjnym takim jak Haskell, wymaga całkowitej zmiany sposobu myślenia o problemie. W Haskellu nie ma pętli, zmienne nie zmieniają swoich wartości… są funkcje, wszechobecna rekurencja i dodatkowe ograniczenia. Z tego powodu wiele osób rezygnuje z języków funkcyjnych już na początku nauki, zanim jeszcze poznają ich możliwości.

Tutoriale

Podstawy języka

edytuj

Następny krok

edytuj
 
Wikibooks
Jest dostępny podręcznik w formie biblii: Haskell (edycja), w wersji do druku.