Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Мы также можем воспринимать значения функторов как значения с добавочным контекстом. Например, значения типа Maybe
обладают дополнительным контекстом того, что вычисления могли окончиться неуспешно. По отношению к спискам контекстом является то, что значение может быть множественным либо отсутствовать. Функция fmap
применяет функцию к значению, сохраняя его контекст.
Если мы хотим сделать конструктор типа экземпляром класса Functor
, он должен иметь сорт *
–>
*
; это значит, что он принимает ровно один конкретный тип в качестве параметра типа. Например, конструктор Maybe
может быть сделан экземпляром, так как он получает один параметр типа для произведения конкретного типа, как, например, Maybe Int
или Maybe String
. Если конструктор типа принимает два параметра, как, например, конструктор Either
, мы должны частично применять конструктор типа до тех пор, пока он не будет принимать только один параметр. Поэтому мы не можем написать определение Functor Either where
, зато можем написать определение Functor (Either a) where
. Затем, если бы мы вообразили, что функция fmap
предназначена только для работы со значениями типа Either a
, она имела бы следующее описание типа:
fmap :: (b –> c) –> Either a b –> Either a c
Как видите, часть Either
a
– фиксированная, потому что частично применённый конструктор типа Either a
принимает только один параметр типа.
Действия ввода-вывода в качестве функторов
К настоящему моменту вы изучили, каким образом многие типы (если быть точным, конструкторы типов) являются экземплярами класса Functor: []
и Maybe
, Either a
, равно как и тип Tree
, который мы создали в главе 7. Вы видели, как можно отображать их с помощью функций на всеобщее благо. Теперь давайте взглянем на экземпляр типа IO
.
Если какое-то значение обладает, скажем, типом IO String
, это означает, что перед нами действие ввода-вывода, которое выйдет в реальный мир и получит для нас некую строку, которую затем вернёт в качестве результата. Мы можем использовать запись <���–
в синтаксисе do
для привязывания этого результата к имени. В главе 8 мы говорили о том, что действия ввода-вывода похожи на ящики с маленькими ножками, которые выходят наружу и приносят нам какое-то значение из внешнего мира. Мы можем посмотреть, что они принесли, но после просмотра нам необходимо снова обернуть значение в тип IO
. Рассматривая эту аналогию с ящиками на ножках, вы можете понять, каким образом тип IO
действует как функтор.
Давайте посмотрим, как же это тип IO
является экземпляром класса Functor
… Когда мы используем функцию fmap
для отображения действия ввода-вывода с помощью функции, мы хотим получить обратно действие ввода-вывода, которое делает то же самое, но к его результирующему значению применяется наша функция. Вот код:
instance Functor IO where
fmap f action = do
result <���– action
return (f result)
Результатом отображения действия ввода-вывода с помощью чего-либо будет действие ввода-вывода, так что мы сразу же используем синтаксис do
для склеивания двух действий и создания одного нового. В реализации для метода fmap
мы создаём новое действие ввода-вывода, которое сначала выполняет первоначальное действие ввода-вывода, давая результату имя result
. Затем мы выполняем return (f result)
. Вспомните, что return
– это функция, создающая действие ввода-вывода, которое ничего не делает, а только возвращает что-либо в качестве своего результата.
Действие, которое производит блок do
, будет всегда возвращать результирующее значение своего последнего действия. Вот почему мы используем функцию return
, чтобы создать действие ввода-вывода, которое в действительности ничего не делает, а просто возвращает применение f result
в качестве результата нового действия ввода-вывода. Взгляните на этот кусок кода:
main = do
line <���– getLine
let line' = reverse line
putStrLn $ "Вы сказали " ++ line' ++ " наоборот!"
putStrLn $ "Да, вы точно сказали " ++ line' ++ " наоборот!"
У пользователя запрашивается строка, и мы отдаём её обратно пользователю, но в перевёрнутом виде. А вот как можно переписать это с использованием функции fmap
:
main = do
line <���– fmap reverse getLine
putStrLn $ "Вы сказали " ++ line ++ " наоборот!"
putStrLn $ "Да, вы точно сказали " ++ line ++ " наоборот!"
Так же как можно отобразить Just "уфф"
с помощью отображения fmap reverse
, получая Just "ффу"
, мы можем отобразить и функцию getLine
с помощью отображения fmap
reverse
. Функция getLine
– это действие ввода-вывода, которое имеет тип IO
String
, и отображение его с помощью функции reverse
даёт нам действие ввода-вывода, которое выйдет в реальный мир и получит строку, а затем применит функцию reverse
к своему результату. Таким же образом, как мы можем применить функцию к тому, что находится внутри коробки Maybe
, можно применить функцию и к тому, что находится внутри коробки IO
, но она должна выйти в реальный мир, чтобы получить что-либо. Затем, когда мы привязываем результат к имени, используя запись <���–
, имя будет отражать результат, к которому уже применена функция reverse
.

Действие ввода-вывода fmap (++"!") getLine
ведёт себя в точности как функция getLine
, за исключением того, что к её результату всегда добавляется строка "!"
в конец!
Если бы функция fmap
работала только с типом IO
, она имела бы тип fmap :: (a –> b) –> IO a –> IO b.
Функция fmap
принимает функцию и действие ввода-вывода и возвращает новое действие ввода-вывода, похожее на старое, за исключением того, что к результату, содержащемуся в нём, применяется функция.
Предположим, вы связываете результат действия ввода-вывода с именем лишь для того, чтобы применить к нему функцию, а затем даёте очередному результату какое-то другое имя, – в таком случае подумайте над использованием функции fmap
. Если вы хотите применить несколько функций к некоторым данным внутри функтора, то можете объявить свою функцию на верхнем уровне, создать анонимную функцию или, в идеале, использовать композицию функций:
import Data.Char
import Data.List
main = do
line <���– fmap (intersperse '-' . reverse . map toUpper) getLine
putStrLn line
Вот что произойдёт, если мы сохраним этот код в файле fmapping_io.hs , скомпилируем, запустим и введём "Эй, привет"
:
$ ./fmapping_io
Интервал:
Закладка: