wSołtysiak BlogPraktycznie o tworzeniu oprogramowania i nie tylko
21 czerwca 2020

Naucz się Haskella tworząc własny projekt

Kilka miesięcy temu, postanowiłem spróbować swoich sił w Haskellu. Na moment chciałem opuścić imperatywny świat “zwykłego” programowania i przekonać się na własnej skórze czy pisanie w języku czysto funkcyjnym jest rzeczywiście tak piękne, jak przedstawiają to różne artykuły i książki.

Naucz się Haskella tworząc własny projekt

W tym wpisie chcę podzielić się z Wami podstawowymi informacjami dotyczącymi postawienia środowiska pod projekt prowadzony w wolnym czasie, ponieważ to właśnie nauka przez napisanie takiego projektu okazała się u mnie skuteczna. Nie mam natomiast zamiaru przedstawiać podstaw Haskella, ponieważ w Internecie istnieje wiele świetnych i darmowych materiałów (polecam gorąco Learn You a Haskell for Great Good!). Na samym początku zastanówmy się jednak czy warto poświęcić swój czas na Haskella.

Dlaczego Haskell?

Wielu programistów podchodzi do Haskella sceptycznie. Ponoć można w tym programować, ale nie przypomina to do końca dobrze znanych języków jak Java czy C++. Legendy głoszą, że próg wejścia jest niesamowicie wysoki, a do tego wszystkiego dochodzą jeszcze jakieś trudne matematyczne pojęcia oraz owiane wielką tajemnicą monady. Ciężko też spotkać osobę, która pracuje na stanowisku “Haskell Developer”.

Haskell is simple

Według mnie takie opinie są spowodowane w głównej mierze koniecznością przejścia na inny paradygmat programowania. Do tego nie trzeba mieć jednak skończonych studiów doktoranckich z matematyki. Muszę jednak przyznać, że przez pierwsze godziny z Haskellem czułem się tak, jakbym cofnął się do momentu, gdy dopiero zaczynałem swoją przygodę z programowaniem.

Jeżeli twoim celem jest wykorzystanie Haskella, żeby dostać ofertę pracy z nieprzyzwoicie wysokim wynagrodzeniem, to niestety jest to nietrafiony pomysł. Aktualnie ciężko jest znaleźć jakąkolwiek ofertę, która wymaga znajomości tego języka. Można natomiast potraktować naukę Haskella jako rozszerzenie ogólnej wiedzy związanej z programowaniem. Dzięki zmianie paradygmatu zaczyna się patrzeć zupełnie inaczej na problemy, które spotykamy podczas codziennej pracy. Część rozwiązań, których się nauczymy, można bez problemu przenieść na inne języki. Nic nie stoi na przeszkodzie, żeby zacząć używać np. algebraicznych struktur danych w JS. Wychodzę z założenia, że kod staje się lepszy dzięki poznawaniu nowych języków, narzędzi i technologii.

Nauka Haskella przez pisanie własnego projektu

Nigdy nie przepadałem za nauką z tutorialem lub książką. Wprowadzanie krok po kroku i dodawanie kodu kawałek po kawałku nie jest złe, ale w takim trybie szybko się nudzę i po kilku dniach nie chcę mi się do tego wracać. Zdecydowanie bardziej sprawdza się u mnie napisanie małego projektu. Taki projekt musi jednak spełniać pewne warunki:

  • Jest ciekawy (dla Ciebie). Chyba nikt nie lubi robić dziesiątej listy TODO w swoim życiu.
  • Posiada mało funkcjonalności. Zawsze lepiej mieć na koncie zakończony projekt, którym można się później pochwalić.
  • Dotyka kilku zagadnień technicznych. Bardzo dobrze, jeżeli projekt wymaga wystawienia prostego API, połączenia z bazą danych lub innego zadania, które często wykonujemy, a które jest jednocześnie oderwane od domeny projektu.
  • Język programowania jest jedyną nowością (lub jedną z nielicznych nowości). Nowy język programowania to wystarczające utrudnienie, nie ma więc sensu dodawać sobie kolejnych rzeczy, ponieważ szybko możemy się zniechęcić.

Dla przykładu, ucząc się Haskella, wykonałem aplikację, która szuka słów kluczowych w podanym tekście. Mimo iż istnieje wiele bardziej rozbudowanych rozwiązań, sama idea była dla mnie bardzo interesująca, ponieważ nigdy wcześniej nie programowałem czegoś, co wymagało przetwarzania języka naturalnego.

Zbiór funkcjonalności został ograniczony do jednej — wyszukiwania słów kluczowych na podstawie wklejonego tekstu. Podczas realizacji aplikacji musiałem przetworzyć tekst za pomocą zewnętrznego API, a następnie utworzyć z niego graf słów, z którego wyciągałem najbardziej powiązane ze sobą słowa. Ostatnim elementem było wystawienie endpointa dla aplikacji frontendowej.

Implementacja dotknęła całkiem sporej liczby zagadnień technicznych jak na tak mały projekt. Jednak każde z tych zagadnień było mi już wcześniej znane w teroii lub wielokrotnie przerobiłem je w praktyce, dzięki czemu mogłem się w 100% skupić się na nauce Haskella.

Przygotowanie projektu

Jeżeli mamy już pomysł to możemy zabrać się za przekuwanie go w kod. Pierwszym krokiem będzie ustawienie działającego środowiska developerskiego.

Postawienie środowiska

Do postawienia środowiska wykorzystamy narzędzie ghcup. Dzięki niemu w wygodny sposób zainstalujemy GHC (Glasgow Haskell Compiler) wraz z cabal-install. Wszystko sprowadza się do uruchomienia jednej komendy (chyba że używasz Windowsa i nie masz zainstalowanego Chocolatey, wtedy czeka Cię trochę dłuższa przygoda), którą znajdziecie na poniższej pod tym linkiem. Następnie wystarczy podążać za instrukcjami, które będą pojawiać się w terminalu.

W pewnym sensie można te narzędzia porównać do narzędzi wykorzystywanych na frontendzie. ghcup możemy potraktować jako nvm, który instaluje i zarządza wersjami GHC (Node.js) oraz cabal-install (npm).

Inicjalizacja oraz uruchomienie projektu

Dzięki wcześniej zainstalowanemu cabal-install możemy w prosty sposób wygenerować podstawowe pliki pod nasz projekt. Robimy to poprzez komendę cabal init --cabal-version-2.4.

Jeżeli chcemy wcześniej dodać do projektu takie informacje jak m.in. strona projektu, licencję to możemy wywołać tryb interaktywny cabal init -i.

Po chwili powinny zostać wygenerowane nowe pliki. Najbardziej będą interesować nas Main.hs oraz nazwaProjektu.cabal. W pierwszym pliku umieszczamy nasz kod. Drugi plik “opisuje” nasz projekt. Można go porównać do package.json. Aby uruchomić nasz projekt wystarczy wpisać cabal run.

Bardziej dociekliwe osoby mogą zauważyć, że cabal oferuje wiele komend, które różnią się tylko prefiksem (np. v1-run, v2-run, new-run). Wszystkie komendy rozpoczynające się od v1- są oznaczone jako przestarzałe. Natomiast komendy oznaczone v2- , new- oraz bez prefiksu wykonują dokładnie to samo. Z perspektywy nowego projektu nie ma się nad czym zastanawiać i wystarczy korzystać z komend bez prefiksów.

Wyszukiwanie zewnętrznych bibliotek

Podczas rozwijania naszego kodu z czasem zachodzi potrzeba skorzystania z zewnętrznej biblioteki. Najprostszym sposobem znalezienia interesującej nas paczki jest skorzystanie z Hackage, czyli publicznego repozytorium dla Haskella.

Czasami występuje jednak sytuacja, w której nie wiemy jakiej paczki dokładnie szukamy. Jeżeli znamy nazwę pojedynczej funkcji lub chociaż jej sygnaturę to możemy użyć Hoogle.

Przykładowo potrzebujemy funkcji, która przyjmuje dowolny tekst, a następnie dzieli go na wyrazy. Wystarczy wpisać w Hoogle Text -> [Text]. Jako wynik dostaniemy listę funkcji z identyczną sygnaturą wraz z krótkim opisem i odnośnikiem do dokumentacji.

Instalowanie zewnętrznych bibliotek

Aby dodać wybraną paczkę do naszego projektu, należy wejść w plik nazwaProjektu.cabal i dodać w sekcji build-depends nazwę biblioteki. Można także określić, jakie konkretnie wersje dopuszczamy poprzez dodanie po nazwie warunku np. aeson >= 1.4.0.0.

Skorzystanie z dodanej zależności polega na zaimportowaniu jej w dowolnym pliku .hs (dla przykładu: import Data.Aeson), a następnie uruchomieniu projektu. Zależność zostanie pobrana, a projekt przebudowany. Od czasu do czasu warto wykonać także komendę cabal update, żeby pobrać najnowszą listę dostępnych bibliotek z repozytorium.

Podsumowanie

Wiedząc, że Haskell nie jest tak trudny, jak go czasami przedstawiają oraz mając przygotowane środowisko, można przystąpić do nauki. Jeżeli programowanie jest twoją pasją, to tworzenie projektu pobocznego w Haskellu będzie świetną metodą do podniesienia swoich umiejętności, która przy okazji przyniesie masę frajdy i satysfakcji.

Powodzenia!