Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Случай с непустым узлом чуть более интересен. Он содержит два поддерева, а также значение. В этом случае мы рекурсивно отображаем левое и правое поддеревья с помощью одной и той же функции f
, используя рекурсивный вызов функции foldMap
. Вспомните, что наша функция foldMap
возвращает в результате одно моноидное значение. Мы также применяем нашу функцию f
к значению в узле. Теперь у нас есть три моноидных значения (два из наших поддеревьев и одно – после применения f
к значению в узле), и нам просто нужно соединить их. Для этой цели мы используем функцию mappend
, и естественным образом левое поддерево идёт первым, затем – значение узла, а потом – правое поддерево [14] Это определение представляет собой один из возможных способов обхода двоичного дерева: «левый – корень – правый». Читатель может самостоятельно реализовать экземпляры для представления других способов обхода двоичных деревьев. – Прим. ред.
.

Обратите внимание, что нам не нужно было предоставлять функцию, которая принимает значение и возвращает моноидное значение. Мы принимаем эту функцию как параметр к foldMap
, и всё, что нам нужно решить, – это где применить эту функцию и как соединить результирующие моноиды, которые она возвращает.
Теперь, когда у нас есть экземпляр класса Foldable
для нашего типа, представляющего дерево, мы получаем функции foldr
и foldl
даром! Рассмотрите вот это дерево:
testTree = Node 5
(Node 3
(Node 1 EmptyTree EmptyTree)
(Node 6 EmptyTree EmptyTree)
)
(Node 9
(Node 8 EmptyTree EmptyTree)
(Node 10 EmptyTree EmptyTree)
)
У него значение 5
в качестве его корня, а его левый узел содержит значение 3
со значениями 1
слева и 6
справа. Правый узел корня содержит значение 9
, а затем значения 8
слева от него и 10
в самой дальней части справа. Используя экземпляр класса Foldable
, мы можем производить всё те же свёртки, что и над списками:
ghci> F.foldl (+) 0 testTree
42
ghci> F.foldl (*) 1 testTree
64800
Функция foldMap
полезна не только для создания новых экземпляров класса Foldable
. Она также очень удобна для превращения нашей структуры в одно моноидное значение. Например, если мы хотим узнать, равно ли какое-либо из чисел нашего дерева 3
, мы можем сделать следующее:
ghci> getAny $ F.foldMap (\x –> Any $ x == 3) testTree
True
Здесь анонимная функция \x –> Any $ x == 3
– это функция, которая принимает число и возвращает моноидное значение: значение Bool
, обёрнутое в тип Any
. Функция foldMap
применяет эту функцию к каждому элементу нашего дерева, а затем превращает получившиеся моноиды в один моноид с помощью вызова функции mappend
. Предположим, мы выполняем следующее:
ghci> getAny $ F.foldMap (\x –> Any $ x > 15) testTree
False
Все узлы нашего дерева будут содержать значение Any False
после того, как к ним будет применена анонимная функция. Но чтобы получить в итоге значение True
, реализация функции mappend
для типа Any
должна принять по крайней мере одно значение True
в качестве параметра. Поэтому окончательным результатом будет False
, что логично, поскольку ни одно значение в нашем дереве не превышает 15
.
Мы также можем легко превратить наше дерево в список, просто используя функцию foldMap
с анонимной функцией \x –> [x]
. Сначала эта функция проецируется на наше дерево; каждый элемент становится одноэлементным списком. Действие функции mappend
, которое имеет место между всеми этими одноэлементными списками, возвращает в результате один список, содержащий все элементы нашего дерева:
ghci> F.foldMap (\x –> [x]) testTree
[1,3,6,5,8,9,10]
Самое классное, что все эти трюки не ограничиваются деревьями. Они применимы ко всем экземплярам класса Foldable
!
13
Пригоршня монад
Когда мы впервые заговорили о функторах в главе 7, вы видели, что они являются полезной концепцией для значений, которые можно отображать. Затем в главе 11 мы развили эту концепцию с помощью аппликативных функторов, которые позволяют нам воспринимать значения определённых типов данных как значения с контекстами и применять к этим значениям обычные функции, сохраняя смысл контекстов.
В этой главе вы узнаете о монадах, которые, по сути, представляют собой расширенные аппликативные функторы, так же как аппликативные функторы являются всего лишь расширенными функторами.
Совершенствуем наши аппликативные функторы

Когда мы начали с функторов, вы видели, что можно отображать разные типы данных с помощью функций, используя класс типов Functor
. Введение в функторы заставило нас задаться вопросом: «Когда у нас есть функция типа a –> b
и некоторый тип данных f a
, как отобразить этот тип данных с помощью функции, чтобы получить значение типа f b
?» Вы видели, как с помощью чего-либо отобразить Maybe a
, список [a]
, IO a
и т. д. Вы даже видели, как с помощью функции типа a –> b
отобразить другие функции типа r –> a
, чтобы получить функции типа r –> b
. Чтобы ответить на вопрос о том, как отобразить некий тип данных с помощью функции, нам достаточно было взглянуть на тип функции fmap
:
fmap :: (Functor f) => (a –> b) –> f a –> f b
А затем нам необходимо было просто заставить его работать с нашим типом данных, написав соответствующий экземпляр класса Functor
.
Потом вы узнали, что возможно усовершенствование функторов, и у вас возникло ещё несколько вопросов. Что если эта функция типа a –> b
уже обёрнута в значение функтора? Скажем, у нас есть Just (*3)
– как применить это к значению Just 5
? Или, может быть, не к Just 5
, а к значению Nothing
? Или, если у нас есть список [(*2),(+4)]
, как применить его к списку [1,2,3]
? Как это вообще может работать?.. Для этого был введён класс типов Applicative
:
(<*>) :: (Applicative f) => f (a –> b) –> f a –> f b
Вы также видели, что можно взять обычное значение и обернуть его в тип данных. Например, мы можем взять значение 1
и обернуть его так, чтобы оно превратилось в Just 1
. Или можем превратить его в [1]
. Оно могло бы даже стать действием ввода-вывода, которое ничего не делает, а просто выдаёт 1
. Функция, которая за это отвечает, называется pure
.
Аппликативное значение можно рассматривать как значение с добавленным контекстом – «причудливое» значение, выражаясь техническим языком. Например, буква 'a'
– это просто обычная буква, тогда как значение Just 'a'
обладает неким добавленным контекстом. Вместо типа Char
у нас есть тип Maybe Char
, который сообщает нам, что его значением может быть буква; но значением может также быть и отсутствие буквы. Класс типов Applicative
позволяет нам использовать с этими значениями, имеющими контекст, обычные функции, и этот контекст сохраняется. Взгляните на пример:
Интервал:
Закладка: