Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Ладно, а что у нас с функцией return
? Она должна принимать значение и помещать его в минимальный контекст, который по-прежнему возвращает это значение в качестве результата. Так каким был бы контекст для значений типа Writer
? Если мы хотим, чтобы сопутствующее моноидное значение оказывало на другие моноидные значения наименьшее влияние, имеет смысл использовать функцию mempty
. Функция mempty
используется для представления «единичных» моноидных значений, как, например, ""
, Sum
0
и пустые строки байтов. Когда мы выполняем вызов функции mappend
между значением mempty
и каким-либо другим моноидным значением, результатом будет это второе моноидное значение. Так что если мы используем функцию return
для создания значения монады Writer
, а затем применяем оператор >>=
для передачи этого значения функции, окончательным моноидным значением будет только то, что возвращает функция. Давайте используем функцию return
с числом 3
несколько раз, только каждый раз будем соединять его попарно с другим моноидом:
ghci> runWriter (return 3 :: Writer String Int)
(3,"")
ghci> runWriter (return 3 :: Writer (Sum Int) Int)
(3,Sum {getSum = 0})
ghci> runWriter (return 3 :: Writer (Product Int) Int)
(3,Product {getProduct = 1})
Поскольку у типа Writer
нет экземпляра класса Show
, нам пришлось использовать функцию runWriter
для преобразования наших значений типа Writer
в нормальные кортежи, которые могут быть показаны в виде строки. Для строк единичным значением является пустая строка. Для типа Sum
это значение 0
, потому что если мы прибавляем к чему-то 0
, это что-то не изменяется. Для типа Product
единичным значением является 1
.
В экземпляре класса Monad
для типа Writer
не имеется реализация для функции fail
; значит, если сопоставление с образцом в нотации do
оканчивается неудачно, вызывается функция error
.
Использование нотации do с типом Writer
Теперь, когда у нас есть экземпляр класса Monad
, мы свободно можем использовать нотацию do
для значений типа Writer
. Это удобно, когда у нас есть несколько значений типа Writer
и мы хотим с ними что-либо делать. Как и в случае с другими монадами, можно обрабатывать их как нормальные значения, и контекст сохраняется для нас. В этом случае все моноидные значения, которые идут в присоединённом виде, объединяются с помощью функции mappend
, а потому отражаются в окончательном результате. Вот простой пример использования нотации do
с типом Writer
для умножения двух чисел:
import Control.Monad.Writer
logNumber :: Int –> Writer [String] Int
logNumber x = Writer (x, ["Получено число: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <���– logNumber 3
b <���– logNumber 5
return (a*b)
Функция logNumber
принимает число и создаёт из него значение типа Writer
. Для моноида мы используем список строк и снабжаем число одноэлементным списком, который просто говорит, что мы получили это число. Функция multWithLog
– это значение типа Writer
, которое перемножает 3
и 5
и гарантирует включение прикреплённых к ним журналов в окончательный журнал. Мы используем функцию return
, чтобы вернуть значение (a*b)
в качестве результата. Поскольку функция return
просто берёт что-то и помещает в минимальный контекст, мы можем быть уверены, что она ничего не добавит в журнал. Вот что мы увидим, если выполним этот код:
ghci> runWriter multWithLog
(15,["Получено число: 3","Получено число: 5"])
Добавление в программы функции журналирования
Иногда мы просто хотим, чтобы некое моноидное значение было включено в каком-то определённом месте. Для этого может пригодиться функция tell
. Она является частью класса типов MonadWriter
и в случае с типом Writer
берёт монадическое значение вроде ["Всё продолжается"]
и создаёт значение типа Writer
, которое возвращает значение-пустышку ()
в качестве своего результата, но прикрепляет желаемое моноидное значение. Когда у нас есть монадическое значение, которое в качестве результата содержит значение ()
, мы не привязываем его к переменной. Вот определение функции multWithLog
с включением некоторых дополнительных сообщений:
multWithLog :: Writer [String] Int
multWithLog = do
a <���– logNumber 3
b <���– logNumber 5
tell ["Перемножим эту парочку"]
return (a*b)
Важно, что вызов return (a*b)
находится в последней строке, потому что результат последней строки в выражении do
является результатом всего выражения do
. Если бы мы поместили вызов функции tell
на последнюю строку, результатом этого выражения do
было бы ()
. Мы бы потеряли результат умножения. Однако журнал остался бы прежним. Вот функция в действии:
ghci> runWriter multWithLog
(15,["Получено число: 3","Получено число: 5","Перемножим эту парочку"])
Добавление журналирования в программы
Алгоритм Евклида – это алгоритм, который берёт два числа и вычисляет их наибольший общий делитель, то есть самое большое число, на которое делятся без остатка оба числа. В языке Haskell уже имеется функция gcd
, которая проделывает это, но давайте реализуем её сами, а затем снабдим её возможностями журналирования. Вот обычный алгоритм:
gcd' :: Int –> Int –> Int
gcd' a b
| b == 0 = a
| otherwise = gcd' b (a `mod` b)
Алгоритм очень прост. Сначала он проверяет, равно ли второе число 0. Если равно, то результатом становится первое число. Если не равно, то результатом становится наибольший общий делитель второго числа и остаток от деления первого числа на второе. Например, если мы хотим узнать, каков наибольший общий делитель 8 и 3, мы просто следуем изложенному алгоритму. Поскольку 3 не равно 0, мы должны найти наибольший общий делитель 3 и 2 (если мы разделим 8 на 3, остатком будет 2). Затем ищем наибольший общий делитель 3 и 2. Число 2 по-прежнему не равно 0, поэтому теперь у нас есть 2 и 1. Второе число не равно 0, и мы выполняем алгоритм ещё раз для 1 и 0, поскольку деление 2 на 1 даёт нам остаток равный 0. И наконец, поскольку второе число равно 0, финальным результатом становится 1. Давайте посмотрим, согласуется ли наш код:
ghci> gcd' 8 3
1
Согласуется. Очень хорошо! Теперь мы хотим снабдить наш результат контекстом, а контекстом будет моноидное значение, которое ведёт себя как журнал. Как и прежде, мы используем список строк в качестве моноида. Поэтому тип нашей новой функции gcd'
должен быть таким:
gcd' :: Int –> Int –> Writer [String] Int
Всё, что осталось сделать, – снабдить нашу функцию журнальными значениями. Вот код:
Читать дальшеИнтервал:
Закладка: