220 likes | 533 Vues
Лекция. Монады. Что такое монады. Монада – философский термин, означающий неделимую и неизменную единицу бытия. В Haskell монада это тип, который может скрыть в себе некий механизм, реализованный внеязыковыми средствами.
E N D
Лекция Монады
Что такое монады Монада – философский термин, означающий неделимую и неизменную единицу бытия. В Haskell монада это тип, который может скрыть в себе некий механизм, реализованный внеязыковыми средствами. Все монады (монадические типы) относятся к классу Monad, основу которого составляют функции композиции, поэтому начнем с понятия "композиция функций".
Композиция функций Композиция – это когда из двух или более функций образуется новая функция, например, так rx = f (gx ) . Такая операция в Хаскеле обозначается точкой. (.):: (b->c)->(a->b)->(a->c) f . g = \x -> f (gx) Функция композиции позволит упростить код.Например, есть функции inc и sqr: inc x = x + 1 sqr x = x * x Вместо main = print (inc (sqr 2)) можно написать main = (print.inc.sqr) 2
Функция применения • Применение функции к аргументам обладает наивысшим приоритетом среди операций, т.е. • sqr 2 + 2 -- это 6, а не 16 • Определив явно операцию применения ($), этот приоритет можно сделать самым низким. • ($):: (a->b)->a->b • infixr 0 $ • f $ x = f x • sqr $ 2 + 2 -- теперь это 16, а не 6 • Заметим, что теперь вместо • main = print (inc (sqr(2 + 3))) • можно писать еще проще • main = print $ inc $ sqr $ 2 + 3
Подобие LINQ В выражениях LINQ запросы выполнялись слева направо, например, число 5 инкрементируем, возводим в квадрат, печатаем: 5 $ inc $ sqr $ print Задача. Изменить определение функции $ так, чтобы добиться желаемого.
Ответ Это можно легко устроить, всего лишь изменив ассоциативность операции $ и порядок следования операндов – слева аргумент, справа функция. ($):: a->(a->b)->b infixl 0 $ x $ f = f x
КлассMonad Все монадические типы принадлежат к классу Monad. class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a В классе две функции композиции, уже знакомая по IO функция return и не существенная функция fail. m – это некий тип, относящийся к классу Monad. Для функций ввода-вывода m = IO.
Связывание >>= Поясним смысл операции >>= :: m a->(a->m b)->m b на примере ввода-вывода. Композицию L>>= R можно истолковать как два последовательных действия, сначала L, потом R, причем R может использовать данные, полученные в результате действия L. Например, ввести строку и тут же ее вывести. m = IO, a = String, b = () L>>=R >>= :: IO String -> (String -> IO()) -> IO() getLine>>= \s -> putStrLn s
Магия связывания getLine>>= \s -> putStrLn s Связывание >>= каким-то образом извлекает из грязного левого значения IO String чистое значение String и передает его в правую функцию, которая вырабатывает из него новое грязное значение. Заметим, чтомы не можемсами объявитьфункциювида(IO a) -> a, поскольку у типа IO a нетконструкторов значений. Поэтому связывание – единственный способ извлечь чистое значение из монадической обертки IO. Из грязного значения чистое извлекается лишь внутри композиции и не выходит за ее пределы.
do – cинтаксический сахар getLine>>= \s -> putStrLn s do s <- getStr putStrLn s Задача. Ввести две строки и вывести сначала вторую, потом первую. Записать это в виде do-нотации и в виде композиции.
Ответ do s1 <- getLine s2 <- getLine putStr s2 putStr s1 getLine >>= \s1 -> getLine >>= \s2 -> putStrLn s2 >> putStrLn s1 Задача. Объявить функцию getInt, подобную функции getLine.
Ответ getInt = do s <- getLine return $ read s getInt::IO Int getInt = getLine >>= \s -> return $ read s
Следование >> >>:: m a->m b->m b Последовательность L>> R можно истолковать как два последовательных действия, сначала L, потом R. Общим результатом композиции является результат второго действия. Никаких данных из L в R не передается. В сущности, следованиеэто частный случайсвязыванияпри условии, что правая функция связывания a->mbигнорирует свой аргумент. Пример. Вывести строку s1, а потом строку s2. main = putStr s1 >> putStrLn s2
Упаковка return Функция return:: a -> m a заворачивает чистое значение в монадическую обертку. Пример. Объявить функцию, которая введет две строки и вернет более длинную из них. f::IO String f = getLine >>= \s1 -> getLine >>= \s2 -> return (if length s1 > length s2 then s1 else s2) main = f >>= \s -> putStrLn s Задача. Записать это же в do-нотации.
Ответ f = do s1 <- getLine s2 <- getLine return (if length s1 > length s2 then s1 else s2) main = do s <- f; putStrLn s
Исключение fail Функция fail :: String -> m a выбрасывает исключение из монадической композиции. Нужна для поддержки do-нотации. f = do s <- getLine if s == "Wow" then fail "Wow!" else return s main = do s <- f; putStrLn s
Монада Maybe Другой пример монадического типа дает стандартный тип Maybe. data Maybe a = Nothing | Just a a – чистое, Maybe a – специальное значение. Maybe позволяет расширить область определения любой функции таким образом, чтобы возвращать специальное значение Nothing на тех аргументах, где ранее функция не была определена (это упрощенный подход к обработке ошибок). fact::Int->Int fact 0 = 1 fact n = n * fact (n-1) mfact::Int->Maybe Int mfact n = if n > 0 then Just (fact n) else Nothing Задача. Вычислить значение n! при помощи функции mfact, а затем возвести его в квадрат.
Ответ mfact::Int->Maybe Int mfact n = if n > 0 then Just (fact n) else Nothing -- композиция ------------ f n = mfact n >>= \f->return (f * f) -- do-нотация --------------------- f n = do f <- mfact; return (f * f)
Конструкторы Maybe В отличие от IO монада Maybe имеет конструкторы. data Maybe a = Nothing | Just a Поэтому можно определить функцию, которая извлечет чистое значение из монадического. Задача. Определить функцию ::Maybe Integer -> Integer. Задача. Определить функцию ::Maybe a -> a g::Maybe Integer -> Integr g Nothing = 0 g (Just a) = a
Списки Списки – тоже монадический тип. Попробуем извлечь значение списка [1,2,3] из монадической обертки и добавить к нему 1. [1,2,3] >>= \x -> return (x + 1)-- композиция >>= do x<-[1,2,3]; return (x + 1) -- do-нотация [x + 1| x<-[1,2,3]] -- list comprehension