Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Но хотя каждая монада – функтор, нам не нужно полагаться на то, что у неё есть экземпляр для класса Functor
, в силу наличия функции liftM
. Функция liftM
берёт функцию и монадическое значение и отображает монадическое значение с помощью функции. Это почти одно и то же, что и функция fmap
! Вот тип функции liftM
:
liftM :: (Monad m) => (a –> b) –> m a –> m b
Сравните с типом функции fmap
:
fmap :: (Functor f) => (a –> b) –> f a –> f b
Если экземпляры классов Functor
и Monad
для типа подчиняются законам функторов и монад, между этими двумя нет никакой разницы (и все монады, которые мы до сих пор встречали, подчиняются обоим). Это примерно как функции pure
и return
, делающие одно и то же, – только одна имеет ограничение класса Applicative
, тогда как другая имеет ограничение Monad
.
Давайте опробуем функцию liftM
:
ghci> liftM (*3) (Just 8)
Just 24
ghci> fmap (*3) (Just 8)
Just 24
ghci> runWriter $ liftM not $ Writer (True, "горох")
(False,"горох")
ghci> runWriter $ fmap not $ Writer (True, "горох")
(False,"горох")
ghci> runState (liftM (+100) pop) [1,2,3,4]
(101,[2,3,4])
ghci> runState (fmap (+100) pop) [1,2,3,4]
(101,[2,3,4])
Вы уже довольно хорошо знаете, как функция fmap
работает со значениями типа Maybe
. И функция liftM
делает то же самое. При использовании со значениями типа Writer
функция отображает первый компонент кортежа, который является результатом. Выполнение функций fmap
или liftM
с вычислением, имеющим состояние, даёт в результате другое вычисление с состоянием, но его окончательный результат изменяется добавленной функцией. Если бы мы не отобразили функцию pop
с помощью (+100)
перед тем, как выполнить её, она бы вернула (1, [2,3,4])
.
Вот как реализована функция liftM
:
liftM :: (Monad m) => (a –> b) –> m a –> m b
liftM f m = m >>= (\x –> return (f x))
Или с использованием нотации do
:
liftM :: (Monad m) => (a –> b) –> m a –> m b
liftM f m = do
x <���– m
return (f x)
Мы передаём монадическое значение m
в функцию, а затем применяем функцию к его результату, прежде чем поместить его обратно в контекст по умолчанию. Ввиду монадических законов гарантируется, что функция не изменит контекст; она изменяет лишь результат, который представляет монадическое значение.
Вы видите, что функция liftM
реализована совсем не ссылаясь на класс типов Functor
. Значит, мы можем реализовать функцию fmap
(или liftM
– называйте, как пожелаете), используя лишь те блага, которые предоставляют нам монады. Благодаря этому можно заключить, что монады, по крайней мере, настолько же сильны, насколько и функторы.
Класс типов Applicative
позволяет нам применять функции между значениями с контекстами, как если бы они были обычными значениями, вот так:
ghci> (+) <$> Just 3 <*> Just 5
Just 8
ghci> (+) <$> Just 3 <*> Nothing
Nothing
Использование этого аппликативного стиля всё упрощает. Операция <$>
– это просто функция fmap
, а операция <*>
– это функция из класса типов Applicative
, которая имеет следующий тип:
(<*>) :: (Applicative f) => f (a –> b) –> f a –> f b
Так что это вроде fmap
, только сама функция находится в контексте. Нам нужно каким-то образом извлечь её из контекста и с её помощью отобразить значение f a
, а затем вновь собрать контекст. Поскольку все функции в языке Haskell по умолчанию каррированы, мы можем использовать сочетание из операций <$>
и <*>
между аппликативными значениями, чтобы применять функции, принимающие несколько параметров.
Однако, оказывается, как и функция fmap
, операция <*>
тоже может быть реализована, используя лишь то, что даёт нам класс типов Monad
. Функция ap
, по существу, – это <*>,
только с ограничением Monad
, а не Applicative
. Вот её определение:
ap :: (Monad m) => m (a –> b) –> m a –> m b
ap mf m = do
f <���– mf
x <���– m
return (fx)
Функция ap
– монадическое значение, результат которого – функция. Поскольку функция, как и значение, находится в контексте, мы берём функцию из контекста и называем её f
, затем берём значение и называем его x
, и, в конце концов, применяем функцию к значению и представляем это в качестве результата. Вот быстрая демонстрация:
ghci> Just (+3) <*> Just 4
Just 7
ghci> Just (+3) `ap` Just 4
Just 7
ghci> [(+1),(+2),(+3)] <*> [10,11]
[11,12,12,13,13,14]
ghci> [(+1),(+2),(+3)] `ap` [10,11]
[11,12,12,13,13,14]
Теперь нам видно, что монады настолько же сильны, насколько и аппликативные функторы, потому что мы можем использовать методы класса Monad
для реализации функций из класса Applicative
. На самом деле, когда обнаруживается, что определённый тип является монадой, зачастую сначала записывают экземпляр класса Monad
, а затем создают экземпляр класса Applicative
, просто говоря, что функция pure
– это return
, а операция <*>
– это ap
. Аналогичным образом, если у вас уже есть экземпляр класса Monad
для чего-либо, вы можете сделать для него экземпляр класса Functor
, просто говоря, что функция fmap
– это liftM
.
Функция liftA2
весьма удобна для применения функции между двумя аппликативными значениями. Она определена вот так:
liftA2 :: (Applicative f) => (a –> b –> c) –> f a –> f b –> f c
liftA2 f x y = f <$> x <*> y
Функция liftM2
делает то же, но с использованием ограничения Monad
. Есть также функции liftM3
, liftM4
и liftM5
.
Вы увидели, что монады не менее сильны, чем функторы и аппликативные функторы – и, хотя все монады, по сути, являются функторами и аппликативными функторами, у них необязательно имеются экземпляры классов Functor
и Applicative
. Мы изучили монадические эквиваленты функций, которые используются функторами и аппликативными функторами.
Функция join
Есть кое-какая пища для размышления: если результат монадического значения – ещё одно монадическое значение (одно монадическое значение вложено в другое), можете ли вы «разгладить» их до одного лишь обычного монадического значения? Например, если у нас есть Just (Just 9)
, можем ли мы превратить это в Just 9
? Оказывается, что любое вложенное монадическое значение может быть разглажено, причём на самом деле это свойство уникально для монад. Для этого у нас есть функция join
. Её тип таков:
join :: (Monad m) => m (m a) –> m a
Значит, функция join
принимает монадическое значение в монадическом значении и отдаёт нам просто монадическое значение; другими словами, она его разглаживает. Вот она с некоторыми значениями типа Maybe
:
Интервал:
Закладка: