Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
ghci> "один" `mappend` "два"
"одиндва"
ghci> "два" `mappend` "один"
"дваодин"
И это нормально. Тот факт, что при умножении выражения 3 * 5
и 5 * 3
дают один и тот же результат, – это просто свойство умножения, но оно не выполняется для большинства моноидов.
Типы Product и Sum
Мы уже изучили один из способов рассматривать числа как моноиды: просто позволить бинарной функции быть оператором *
, а единичному значению – быть 1
. Ещё один способ для чисел быть моноидами состоит в том, чтобы в качестве бинарной функции выступал оператор +
, а в качестве единичного значения – значение 0
:
ghci> 0 + 4
4
ghci> 5 + 0
5
ghci> (1 + 3) + 5
9
ghci> 1 + (3 + 5)
9
Законы моноидов выполняются, потому что если вы прибавите 0 к любому числу, результатом будет то же самое число. Сложение также ассоциативно, поэтому здесь у нас нет никаких проблем.
Итак, в нашем распоряжении два одинаково правомерных способа для чисел быть моноидами. Какой же способ выбрать?.. Ладно, мы не обязаны выбирать! Вспомните, что когда имеется несколько способов определения для какого-то типа экземпляра одного и того же класса типов, мы можем обернуть этот тип в декларацию newtype
, а затем сделать для нового типа экземпляр класса типов по-другому. Можно совместить несовместимое.
Модуль Data.Monoid
экспортирует для этого два типа: Product
и Sum
.
Product
определён вот так:
newtype Product a = Product { getProduct :: a }
deriving (Eq, Ord, Read, Show, Bounded)
Это всего лишь обёртка newtype
с одним параметром типа наряду с некоторыми порождёнными экземплярами. Его экземпляр для класса Monoid
выглядит примерно так:
instance Num a => Monoid (Product a) where
mempty = Product 1
Product x `mappend` Product y = Product (x * y)
Значение mempty
– это просто 1, обёрнутая в конструктор Product
. Функция mappend
производит сопоставление конструктора Product
с образцом, перемножает два числа, а затем оборачивает результирующее число. Как вы можете видеть, имеется ограничение класса Num a
. Это значит, что Product a
является экземпляром Monoid
для всех значений типа a
, для которых уже имеется экземпляр класса Num
. Для того чтобы использовать тип Product a
в качестве моноида, мы должны произвести некоторое оборачивание и разворачивание newtype
:
ghci> getProduct $ Product 3 `mappend` Product 9
27
ghci> getProduct $ Product 3 `mappend` mempty
3
ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2
24
ghci> getProduct . mconcat . map Product $ [3,4,2]
24
Тип Sum
определён в том же духе, что и тип Product
, и экземпляр тоже похож. Мы используем его точно так же:
ghci> getSum $ Sum 2 `mappend` Sum 9
11
ghci> getSum $ mempty `mappend` Sum 3
3
ghci> getSum . mconcat . map Sum $ [1,2,3]
6
Типы Any и All
Ещё одним типом, который может действовать как моноид двумя разными, но одинаково допустимыми способами, является Bool
. Первый способ состоит в том, чтобы заставить функцию ||
, которая представляет собой логическое ИЛИ, действовать как бинарная функция, используя False
в качестве единичного значения. Если при использовании логического ИЛИ какой-либо из параметров равен True
, функция возвращает True
; в противном случае она возвращает False
. Поэтому если мы используем False
в качестве единичного значения, операция ИЛИ вернёт False
при использовании с False
– и True
при использовании с True
. Конструктор newtype Any
аналогичным образом имеет экземпляр класса Monoid
. Он определён вот так:
newtype Any = Any { getAny :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
А его экземпляр выглядит так:
instance Monoid Any where
mempty = Any False
Any x `mappend` Any y = Any (x || y)
Он называется Any
, потому что x `mappend` y
будет равно True
, если любое из этих двух значений равно True
. Даже когда три или более значений Bool
, обёрнутых в Any
, объединяются с помощью функции mappend
, результат будет содержать True
, если любое из них равно True
.
ghci> getAny $ Any True `mappend` Any False
True
ghci> getAny $ mempty `mappend` Any True
True
ghci> getAny . mconcat . map Any $ [False, False, False, True]
True
ghci> getAny $ mempty `mappend` mempty
False
Другой возможный вариант экземпляра класса Monoid
для типа Bool
– всё как бы наоборот: заставить оператор &&
быть бинарной функцией, а затем сделать значение True
единичным значением. Логическое И вернёт True
, только если оба его параметра равны True
.
Это объявление newtype
:
newtype All = All { getAll :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
А это экземпляр:
instance Monoid All where
mempty = All True
All x `mappend` All y = All (x && y)
Когда мы объединяем значения типа All
с помощью функции mappend
, результатом будет True
только в случае, если все значения, использованные в функции mappend
, равны True
:
ghci> getAll $ mempty `mappend` All True
True
ghci> getAll $ mempty `mappend` All False
False
ghci> getAll . mconcat . map All $ [True, True, True]
True
ghci> getAll . mconcat . map All $ [True, True, False]
False
Так же, как при использовании умножения и сложения, мы обычно явно указываем бинарные функции вместо оборачивания их в значения newtype
и последующего использования функций mappend
и mempty
. Функция mconcat
кажется полезной для типов Any
и All
, но обычно проще использовать функции or
и and
. Функция or
принимает списки значений типа Bool
и возвращает True
, если какое-либо из них равно True
. Функция and
принимает те же значения и возвращает значение True
, если все из них равны True
.
Моноид Ordering
Помните тип Ordering
? Он используется в качестве результата при сравнении сущностей и может иметь три значения: LT
, EQ
и GT
, которые соответственно означают «меньше, чем», «равно» и «больше, чем».
ghci> 1 `compare` 2
LT
ghci> 2 `compare` 2
EQ
ghci> 3 `compare` 2
GT
При использовании чисел и значений типа Bool
поиск моноидов сводился к просмотру уже существующих широко применяемых функций и их проверке на предмет того, проявляют ли они какое-либо поведение, присущее моноидам. При использовании типа Ordering
нам придётся приложить больше старания, чтобы распознать моноид. Оказывается, его экземпляр класса Monoid
настолько же интуитивен, насколько и предыдущие, которые мы уже встречали, и кроме того, весьма полезен:
Интервал:
Закладка: