Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
ghci> join (Just (Just 9))
Just 9
ghci> join (Just Nothing)
Nothing
ghci> join Nothing
Nothing
В первой строке – успешное вычисление как результат успешного вычисления, поэтому они оба просто соединены в одно большое успешное вычисление. Во второй строке значение Nothing
представлено как результат значения Just
. Всякий раз, когда мы раньше имели дело со значениями Maybe
и хотели объединить несколько этих значений – будь то с использованием операций <*>
или >>=
– все они должны были быть значениями конструктора Just
, чтобы результатом стало значение Just
. Если на пути возникала хоть одна неудача, то и результатом являлась неудача; нечто аналогичное происходит и здесь. В третьей строке мы пытаемся разгладить то, что возникло вследствие неудачи, поэтому результат – также неудача.
Разглаживание списков осуществляется довольно интуитивно:
ghci> join [[1,2,3],[4,5,6]]
[1,2,3,4,5,6]
Как вы можете видеть, функция join
для списков – это просто concat
. Чтобы разгладить значение монады Writer
, результат которого сам является значением монады Writer
, нам нужно объединить моноидное значение с помощью функции mappend
:
ghci> runWriter $ join (Writer (Writer (1, "aaa"), "bbb"))
(1,"bbbaaa")
Внешнее моноидное значение "bbb"
идёт первым, затем к нему конкатенируется строка "aaa"
. На интуитивном уровне, когда вы хотите проверить результат значения типа Writer
, сначала вам нужно записать его моноидное значение в журнал, и только потом вы можете посмотреть, что находится внутри него.
Разглаживание значений монады Either
очень похоже на разглаживание значений монады Maybe
:
ghci> join (Right (Right 9)) :: Either String Int
Right 9
ghci> join (Right (Left "ошибка")) :: Either String Int
Left "ошибка"
ghci> join (Left "ошибка") :: Either String Int
Left "ошибка"
Если применить функцию join
к вычислению с состоянием, результат которого является вычислением с состоянием, то результатом будет вычисление с состоянием, которое сначала выполняет внешнее вычисление с состоянием, а затем результирующее. Взгляните, как это работает:
ghci> runState (join (state $ \s –> (push 10, 1:2:s))) [0,0,0]
((),[10,1,2,0,0,0])
Здесь анонимная функция принимает состояние, помещает 2
и 1
в стек и представляет push 10
как свой результат. Поэтому когда всё это разглаживается с помощью функции join
, а затем выполняется, всё это выражение сначала помещает значения 2
и 1
в стек, а затем выполняется выражение push 10
, проталкивая число 10
на верхушку.
Реализация для функции join
такова:
join :: (Monad m) => m (m a) –> m a
join mm = do
m <���– mm
m
Поскольку результат mm
является монадическим значением, мы берём этот результат, а затем просто помещаем его на его собственную строку, потому что это и есть монадическое значение. Трюк здесь в том, что когда мы вызываем выражение m
<���–
mm
, контекст монады, в которой мы находимся, будет обработан. Вот почему, например, значения типа Maybe
дают в результате значения Just
, только если и внешнее, и внутреннее значения являются значениями Just
. Вот как это выглядело бы, если бы значение mm
было заранее установлено в Just (Just 8)
:
joinedMaybes :: Maybe Int
joinedMaybes = do
m <���– Just (Just 8)
m
Наверное, самое интересное в функции join
– то, что для любой монады передача монадического значения в функцию с помощью операции >>=
представляет собой то же самое, что и просто отображение значения с помощью этой функции, а затем использование функции join
для разглаживания результирующего вложенного монадического значения! Другими словами, выражение m >>= f
– всегда то же самое, что и join (fmap f m)
. Если вдуматься, это имеет смысл.
При использовании операции >>=
мы постоянно думаем, как передать монадическое значение функции, которая принимает обычное значение, а возвращает монадическое. Если мы просто отобразим монадическое значение с помощью этой функции, то получим монадическое значение внутри монадического значения. Например, скажем, у нас есть Just 9
и функция \x –> Just (x+1)
. Если с помощью этой функции мы отобразим Just 9
, у нас останется Just (Just 10)
.
То, что выражение m >>= f
всегда равно join (fmap f m)
, очень полезно, если мы создаём свой собственный экземпляр класса Monad
для некоего типа. Это связано с тем, что зачастую проще понять, как мы бы разгладили вложенное монадическое значение, чем понять, как реализовать операцию >>=
.

Ещё интересно то, что функция join
не может быть реализована, всего лишь используя функции, предоставляемые функторами и аппликативными функторами. Это приводит нас к заключению, что монады не просто сопоставимы по своей силе с функторами и аппликативными функторами – они на самом деле сильнее , потому что с ними мы можем делать больше, чем просто с функторами и аппликативными функторами.
Функция filterM
Функция filter
– это просто хлеб программирования на языке Haskell (при том что функция map
– масло). Она принимает предикат и список, подлежащий фильтрации, а затем возвращает новый список, в котором сохраняются только те элементы, которые удовлетворяют предикату. Её тип таков:
filter :: (a –> Bool) –> [a] –> [a]
Предикат берёт элемент списка и возвращает значение типа Bool
. А вдруг возвращённое им значение типа Bool
было на самом деле монадическим? Что если к нему был приложен контекст?.. Например, каждое значение True
или False
, произведённое предикатом, имело также сопутствующее моноидное значение вроде ["Принято число 5"]
или ["3 слишком мало"]
? Если бы это было так, мы бы ожидали, что к результирующему списку тоже прилагается журнал всех журнальных значений, которые были произведены на пути. Поэтому если бы к списку, возвращённому предикатом, возвращающим значение типа Bool
, был приложен контекст, мы ожидали бы, что к результирующему списку тоже прикреплён некоторый контекст. Иначе контекст, приложенный к каждому значению типа Bool
, был бы утрачен.
Функция filterM
из модуля Control.Monad
делает именно то, что мы хотим! Её тип таков:
filterM :: (Monad m) => (a –> m Bool) –> [a] –> m [a]
Предикат возвращает монадическое значение, результат которого – типа Bool
, но поскольку это монадическое значение, его контекст может быть всем чем угодно, от возможной неудачи до недетерминированности и более! Чтобы обеспечить отражение контекста в окончательном результате, результат тоже является монадическим значением.
Интервал:
Закладка: