Podczas przygotowywania mojego warsztatu na GirlzCamp postanowiłem napisać sobie kilka monad w F#, w tym spróbować napisać transformator monad (co w ogólności mi się nie udało). Podczas moich zmagań natrafiłem na bibliotekę FSharpPlus, która jest mega!!
Przede wszystkim, jednym z większych problemów z jakimi się borykałem to sprawa operatora bind >>=. W Haskellu, dzięki klasom typów, można używać tego operatora do wszystkich monad, które instancjonują klasę Monad. W F# jest to nieco trudniejsze.
Okazuje się, że F# ma dwa typy generycznych argumentów typów. Najczęściej używany to 'T (z apostrofem), czyli typy ogólnie generyczne, zachowujące się tak jak generyki .NETowe. Ale jest jeszcze drugi typ - ^T (z daszkiem), który określa generyczny typ, rozwiązywany w trakcie kompilacji. Może być użyty tylko dla funkcji, które są inline i podczas kompilacji taka funkcja jest zastępowana na wywołanie konretnej funkcji, być może specyficznej dla danego typu.
Odpowiednie użycie funkcji inline i tych argumentów typowych rozwiązywanych w trackie kompilacji, pozwala na utworzenie generycznego operatora >>=, którego można używać dla każdej monady, pod warunkiem, że jej typ monadyczny zawiera
static member (>>=) : '``Monad<'T>`` -> ('T -> '``Monad<'U>``) -> '``Monad<'U>``
Także okazuje się, że F# pozwala na więcej niż by się na pierwszy rzut oka wydawało, ale składnia do tego jest strasznie nieintuicyjna.
W bibliotece FSharpPlus znajdziemy definicje kilku poręcznych funkcji oraz gotowych monad.
result x- funkcja o znaczeniureturn, które w F# jest słowem kluczowym(>>=) x f- bind-
map f x- dlaxbędącego Functorem, aplikujemy funkcjęfdo wartości w środkuxnp:let h = map ((+) 2) (Some 3) //h = Some 5
monad { }- środowisko do pracy z każdą monadą(<*>) : f<'a -> 'b> -> f<'a> -> f<'b>- gdziefto pewien Functor- Implementacja
MonadiFunctordla wbudowanych typówOption,Result,Choice - Monady
State,Writer,Reader,Contoraz ich transformatory
Więcej inormacji znajdziecie w repozytorium na GitHubie, aczkolwiek, ze względu na to, że spora część tej biblioteki to nieziemskie obejścia, jej kod jest mało czytelny.
Nie mniej jednak jesteśmy w stanie napisać coś takiego (przykład poniżej). Korzystanie z biblioteki wymaga dużej ilości adnotacji typów, żeby kompilator nie narzekał, więc nie jestem pewny czy będę ją wykorzystywał w projektach. Mam nadzieję, że dyskusja o wprowadzeniu klas typów do F# posunie się niedługo do przodu i zobaczymy używalne rozwiązanie.
