Vstup, výstup a monády

Ciele

  1. Naučiť sa vytvárať vykonateľné programy.
  2. Oboznámiť sa s operáciami pre vstup a výstup.
  3. Oboznámiť sa s konceptom monád.

Postup

Krok 1: Spustiteľný program

Spustiteľný program v jazyku Haskell musí obsahovať funkciu main :: IO ().

Úloha 1.1

Vytvorte nový súbor hello.hs, obsahujúci funkciu main:

main :: IO ()
main = putStrLn "Hello world!"

Skompilujte tento program:

ghc hello.hs

Krok 2: Vstupné a výstupné akcie

Úloha 2.1

Upravte program na zobrazovanie histogramu tak, aby čítal zoznam čísel zo súboru, názov ktorého bude zadaný ako argument príkazového riadku.

Poznámka

Použite funkcie pre prácu so súbormi a funkciu getArgs. Potrebujete tiež pridať do kódu:

import System.Environment

Krok 3: Interakcia

Úloha 3.1

Vytvorte aplikáciu pre hádanie mysleného čísla: Nech počítač si myslí číslo a používateľ háda myslené číslo. Počítač nech po nesprávnom odhade povie, či je myslené číslo väčšie alebo menšie ako hádané.

Aby sme mohli používať doplňujúcu knižnicu random, vytvoríme nový projekt. V novom adresári guess spustíte príkaz pre vytvorenie projektu:

cabal init

Na všetky otázky stačí dať predvolenú odpoveď.

V súbore guess.cabal pridajte závislosť na balík random:

    -- Other library packages from which modules are imported.
    build-depends:    base ^>=4.18.2.1,
                      random

Môžete použiť pomocnú funkciu randNumber na generovanie náhodného čísla na základe špecifikovaného intervalu a odovzdaného generátora:

module Main where
import System.Random

main :: IO ()
main = do
    number <- randNumber 1 10
    putStrLn ("Hello, " ++ show number ++ "!")


randNumber :: Int -> Int -> IO Int
randNumber a b = getStdGen >>= \gen -> return (fst (randomR (a, b) gen))

Teraz na spúšťanie programu vám stačí použiť príkaz:

cabal run

Krok 4: Monády

Typ IO je len jedným z monadických typov. Haskell však definuje viacero takýchto typov. Pre všetky z nich je možné používať operátory >>=, >> na spájanie operácií a funkciu return na zabalenie hodnoty do monády. Pritom to, čo sa vykoná pri spájaní operácii pomocou >>= je definované typom. Pre IO je to sekvenčné vykonávanie akcií, ale napríklad pre zoznam ([]), ktorý je tiež monádou, je to vykonanie ďalšej operácie na každom elemente z výsledku predchádzajúcej operácie a spojenie výsledkov do jedného zoznamu (xs >>= f = concat (map f xs)).

generation = replicate 3
test1 = ["bunny"] >>= generation
test2 = ["bunny"] >>= generation >>= generation

Ďalším príkladom monády je typ Maybe. V jeho prípade operátor >>= prepája viacero operácií vracajúcich Maybe tak, že ak niektorá z nich vráti Nothing, potom celá reťaz vráti Nothing. Majme, napríklad, funkciu getTaxOwed, ktorá vyhľadá veľkosť dane naprieč niekoľkých databáz pomocou funkcie lookup (kompletný kód s databázami).

getTaxOwed :: String -> Maybe Double
getTaxOwed name =
    case lookup name phonebook of
        (Just number) -> case lookup number governmentDatabase of
            (Just registration) -> lookup registration taxDatabase
            Nothing -> Nothing
        Nothing -> Nothing

Úloha 4.1

Zjednodušte funkciu getTaxOwed s využitím monadického operátora >>=.

Použitie operátora >>= v prípade typov Maybe a Either umožňuje dosiahnuť efekt, podobný spracovávaniu výnimiek (exception handling). Prípadnú chybu nie je nutné ošetrovať v každom výraze kde môže vzniknúť, ale len na jednom mieste.

Zdroje

  1. A Gentle Introduction to Haskell: Input/Output
  2. HaskellWiki: Introduction to IO
  3. A Gentle Introduction to Haskell: About Monads

Doplňujúce zdroje

  1. Haskell / Understanding monads / Maybe
  2. Haskell / Understanding monads / List