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: 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 3.1

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

Použitie operátora >>= v prípade typoc 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 vzniknuť, ale len na jednom mieste.

Krok 4: Zadanie

Spojme dva použitia monád pri riešení zadania — interakcie s hráčom v hre Míny. Použijeme monádu IO pre realizáciu vstupu a výstupu, a monádu Either pre ošetrovanie chýb.

Úloha 4.1

Implementujte modul Minesweeper.HumanSolver z projektu.

Hlavná funkcia humanSolver bude riešiť vstup a výstup. V prípade chybného vstupu tiež pomocou rekurzie zabezpečí opakované čítanie vstupu.

-- | Read a command from a user.
humanSolver :: Solver
humanSolver board = do
    putStrLn "Please enter your choice: <O A1> for open, <M A1> for mark"
    input <- getLine
    ...

O spracovanie vstupu od používateľa sa bude starať samostatná funkcia parseInput. Ta dostane hraciu plochu (kvôli validácií ťahov) a reťazec zadaný používateľom. Jej výsledkom je buď zadaný ťah, alebo hlásenie o chybe.

parseInput :: Board -> String -> Either String Move

Samotná funkcia však iba normalizuje vstup a kontroluje, či nie je prázdny. O jednotlivé kroky spracovania sa budú starať pomocné funkcie:

normalizeInput :: String -> String
parseMove :: Board -> String -> Either String Move  -- Expects nonempty normalized string
parseCommand :: Char -> Either String (Pos -> Move)
parsePosition :: String -> Either String Pos

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