FP / Vstup, výstup a monády

Vstup, výstup a monády

Funkcionálne programovanie

Ako robiť I/O v čistých funkciách?

Príklad

substractInputs :: Int
substractInputs = getInt - getInt
  where
    getInt :: Int
    getInt = read (getLine)

Bude to fungovať?

Ako to vyriešiť?

Nemôžem vrátiť výsledok I/O

Môžem vrátiť samotnú I/O akciu

IO

Namiesto vykonánia operácie, vrátime ju ako výsledok.

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

Typ IO a

A value of type IO a is a computation which, when performed, does some I/O before returning a value of type a.

There is really only one way to “perform” an I/O action: bind it to Main.main in your program. When your program is run, the I/O will be performed.

Prelude documentation

IO bez hodnoty

putChar :: Char -> IO ()
putStr :: String -> IO ()
putStrLn :: String -> IO ()
print :: Show a => a -> IO ()

Typ ()

IO produkujúce hodnotu

getChar :: IO Char
getLine :: IO String
getContents :: IO String

Príklad

main :: IO ()
main = putStr (histogram values)

Jedna akcia nestačí

Spojiť viacero akcií

(>>) :: IO a -> IO b -> IO b
main = putStrLn "Hello, what is your name?"
      >> getLine
      >> putStrLn ("Hello, world!")

Čo s hodnotou?

Spojiť viacero akcií

(>>) :: IO a -> IO b -> IO b
main = putStrLn "Hello, what is your name?"
      >> getLine
      >> putStrLn ("Hello, world!")

Bind

(>>=) :: IO a -> (a -> IO b) -> IO b
main = putStrLn "Hello, what is your name?"
      >> getLine
      >>= \name -> putStrLn ("Hello, " ++ name ++ "!")

Return

return :: a -> IO a

Príklad

substractInputs :: IO Int
substractInputs =
    getInt
    >>= \ x -> getInt
    >>= \ y -> return (x - y)
  where
    getInt :: IO Int
    getInt = getLine >>= \ line -> return (read line)

do notácia

substractInputs :: IO Int
substractInputs = do
    x <- getInt
    y <- getInt
    return (x - y)
  where
    getInt :: IO Int
    getInt = getLine >>= return . read
main = putStrLn "Hello, what is your name?"
      >> getLine
      >>= \name -> putStrLn ("Hello, " ++ name ++ "!")
main = do
    putStrLn "Hello, what is your name?"
    name <- getLine
    putStrLn ("Hello, " ++ name ++ "!")
main = putStrLn "Hello, what is your name?"
      >> getLine
      >>= \name -> putStrLn ("Hello, " ++ name ++ "!")
main = do {
    putStrLn "Hello, what is your name?";
    name <- getLine;
    putStrLn ("Hello, " ++ name ++ "!")
}

IO a ostatný kód

Monády

Čo je monáda?

Monáda je monoid v kategórii endofunktorov

Poďme na to inak

Príklad

studentStatus :: String -> String
studentStatus name = lookup status statusNames
  where
    status = lookup id statuses
    id = lookup name studentIDs

Problém: Maybe

Riešenie

studentStatus :: String -> Maybe String
studentStatus name =
    case lookup name studentIDs of
        Just id -> case lookup id statuses of
            Just status -> lookup status statusNames
            Nothing -> Nothing
        Nothing -> Nothing

Ako to zjednodušiť?

>>=
vieme preťažiť!

Použijeme >>=

studentStatus :: String -> Maybe String
studentStatus name =
        lookup name studentIDs
    >>= \ id -> lookup id statuses
    >>= \ status -> lookup readableStatus status

Alebo inak

studentStatus :: String -> Maybe String
studentStatus name = do
    id <- lookup name studentIDs
    status <- lookup id statuses
    lookup readableStatus status

Monáda

class  Monad m  where
    (>>=)   :: m a -> (a -> m b) -> m b
    (>>)    :: m a -> m b -> m b
    return  :: a -> m a

Maybe

instance  Monad Maybe  where
    (Just x) >>= k  = k x
    Nothing  >>= _  = Nothing
    return          = Just

Either

instance Monad (Either e) where
    Left  l >>= _  = Left l
    Right r >>= k  = k r
    return         = Right

Zoznamy

instance Monad []  where
    xs >>= f  = concat (map f xs)
    return x  = [x]

Ďalšie príklady

Analógia

Java

Zákony monád

Alebo ešte inak

Aplikácia funkcie na hodnotu

square 2
4

Aplikácia funkcie na hodnotu v zozname

map square [2]
[4]

Vieme to zovšeobecniť?

Funktor

fmap square [2]
➜ [4]
fmap square (Just 2)
➜ Just 4
fmap square Nothing
➜ Nothing

Funktor

class Functor f where
    fmap :: (a -> b) -> f a -> f b
    (<$) :: a -> f b -> f a

(<$>) = fmap

Aj funkcia je Funktor

instance Functor ((->) r) where
    fmap f g = f . g
let squareDouble = fmap square (*2)
squareDouble 5
➜ 100

Applicative

Just square <*> Just 2
➜ Just 4
 [(*2), (+3)] <*> [1, 2, 3]
➜ [2, 4, 6, 4, 5, 6]

Princíp