Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
ghci> let a = fmap (*) [1,2,3,4]
ghci> :t a
a :: [Integer –> Integer]
ghci> fmap (\f –> f 9) a
[9,18,27,36]
Но что если у нас есть значение функтора Just (3 *)
и значение функтора Just 5
, и мы хотим извлечь функцию из Just (3 *)
и отобразить с её помощью Just 5
? С обычными функторами у нас этого не получится, потому что они поддерживают только отображение имеющихся функторов с помощью обычных функций. Даже когда мы отображали функтор, содержащий функции, с помощью анонимной функции \f –> f 9
, мы делали именно это и только это. Но используя то, что предлагает нам функция fmap
, мы не можем с помощью функции, которая находится внутри значения функтора, отобразить другое значение функтора. Мы могли бы произвести сопоставление конструктора Just
по образцу для извлечения из него функции, а затем отобразить с её помощью Just 5
, но мы ищем более общий и абстрактный подход, работающий с функторами.
Поприветствуйте аппликативные функторы
Итак, встречайте класс типов Applicative
, находящийся в модуле Control.Applicative
!.. Он определяет две функции: pure
и <*>
. Он не предоставляет реализации по умолчанию для какой-либо из этих функций, поэтому нам придётся определить их обе, если мы хотим, чтобы что-либо стало аппликативным функтором. Этот класс определён вот так:
class (Functor f) => Applicative f where
pure :: a –> f a
(<*>) :: f (a –> b) –> f a –> f b
Простое определение класса из трёх строк говорит нам о многом!.. Первая строка начинается с определения класса Applicative
; также она вводит ограничение класса. Ограничение говорит, что если мы хотим определить для типа экземпляр класса Applicative
, он, прежде всего, уже должен иметь экземпляр класса Functor
. Вот почему, когда нам известно, что конструктор типа принадлежит классу Applicative
, можно смело утверждать, что он также принадлежит классу Functor
, так что мы можем применять к нему функцию fmap
.
Первый метод, который он определяет, называется pure
. Его сигнатура выглядит так: pure :: a –> f a
. Идентификатор f
играет здесь роль нашего экземпляра аппликативного функтора. Поскольку язык Haskell обладает очень хорошей системой типов и притом всё, что может делать функция, – это получать некоторые параметры и возвращать некоторое значение, мы можем многое сказать по объявлению типа, и данный тип – не исключение.
Функция pure
должна принимать значение любого типа и возвращать аппликативное значение с этим значением внутри него. Словосочетание «внутри него» опять вызывает в памяти нашу аналогию с коробкой, хотя мы и видели, что она не всегда выдерживает проверку. Но тип a –> f a
всё равно довольно нагляден. Мы берём значение и оборачиваем его в аппликативное значение, которое содержит в себе это значение в качестве результата. Лучший способ представить себе функцию pure
– это сказать, что она берёт значение и помещает его в некий контекст по умолчанию (или чистый контекст) – минимальный контекст, который по-прежнему возвращает это значение.
Оператор <*>
действительно интересен. У него вот такое определение типа:
f (a –> b) –> f a –> f b
Напоминает ли оно вам что-нибудь? Оно похоже на сигнатуру fmap
::
(a
–>
b)
–>
f
a
–>
f
b
. Вы можете воспринимать оператор <*>
как разновидность расширенной функции fmap
. Тогда как функция fmap
принимает функцию и значение функтора и применяет функцию внутри значения функтора, оператор <*>
принимает значение функтора, который содержит в себе функцию, и другой функтор – и извлекает эту функцию из первого функтора, затем отображая с её помощью второй.
Аппликативный функтор Maybe
Давайте взглянем на реализацию экземпляра класса Applicative
для типа Maybe
:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
Опять же из определения класса мы видим, что идентификатор f
, который играет роль аппликативного функтора, должен принимать один конкретный тип в качестве параметра. Поэтому мы пишем instance Applicative Maybe where
вместо instance Applicative (Maybe a) where
.
Далее, у нас есть функция pure
. Вспомните, что функция должна что-то принять и обернуть в аппликативное значение. Мы написали pure = Just
, потому что конструкторы данных вроде Just
являются обычными функциями. Также можно было бы написать pure x = Just x
.
Наконец, у нас есть определение оператора <*>
. Извлечь функцию из значения Nothing
нельзя, поскольку внутри него нет функции. Поэтому мы говорим, что если мы пробуем извлечь функцию из значения Nothing
, результатом будет то же самое значение Nothing
.
В определении класса Applicative
есть ограничение класса Functor
– значит, мы можем считать, что оба параметра оператора <*>
являются значениями функтора. Если первым аргументом выступает не значение Nothing
, а Just
с некоторой функцией внутри, то мы говорим, что с помощью данной функции хотим отобразить второй параметр. Этот код также заботится о случае, когда вторым аргументом является значение Nothing
, потому что его отображение с помощью любой функции при использовании метода fmap
вернёт всё то же Nothing
. Итак, в случае с типом Maybe
оператор <*>
извлекает функцию из значения слева, если это Just
, и отображает с её помощью значение справа. Если какой-либо из параметров является значением Nothing
, то и результатом будет Nothing
.
Теперь давайте это опробуем:
ghci> Just (+3) <*> Just 9
Just 12
ghci> pure (+3) <*> Just 10
Just 13
ghci> pure (+3) <*> Just 9
Just 12
ghci> Just (++"ха-ха") <*> Nothing Nothing
ghci> Nothing <*> Just "во-от"
Nothing
Вы видите, что выполнение выражений pure (+3)
и Just (+3)
в данном случае – одно и то же. Используйте функцию pure
, если имеете дело со значениями типа Maybe
в аппликативном контексте (если вы используете их с оператором <*>
); в противном случае предпочитайте конструктор Just
.
Первые четыре введённых строки демонстрируют, как функция извлекается, а затем используется для отображения; но в данном случае этого можно было добиться, просто применив не обёрнутые функции к функторам. Последняя строка любопытна тем, что мы пытаемся извлечь функцию из значения Nothing
, а затем отображаем с её помощью нечто, что в результате даёт Nothing
.
Когда вы отображаете функтор с помощью функции при использовании обычных функторов, вы не можете извлечь результат каким-либо общим способом, даже если результатом является частично применённая функция. Аппликативные функторы, с другой стороны, позволяют вам работать с несколькими функторами, используя одну функцию.
Читать дальшеИнтервал:
Закладка: