Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
• isAlreadyExistsError
(файл уже существует);
• isDoesNotExistError
(файл не существует);
• isAlreadyInUseError
(файл уже используется);
• isFullError
(не хватает места на диске);
• isEOFError
(достигнут конец файла);
• isIllegalOperation
(выполнена недопустимая операция);
• isPermissionError
(недостаточно прав доступа).
Пользуясь этими предикатами, можно написать примерно такой обработчик:
handler :: IOException -> IO ()
handler e
| isDoesNotExistError e = putStrLn "Файл не существует!"
| isPermissionError e = putStrLn "Не хватает прав доступа!"
| isFullError e = putStrLn "Освободите место на диске!"
| isIllegalOperation e = putStrLn "Караул! Спасите!"
| otherwise = ioError e
Убедитесь, что вы перевызываете исключение, если оно не подходит под ваши критерии; в противном случае ваша программа иногда будет «падать» молча, что крайне нежелательно.
Модуль System.IO.Error
также экспортирует функции, которые позволяют нам получать атрибуты исключения, например дескриптор файла, вызвавшего исключение, или имя файла. Все эти функции начинаются с префикса ioe
; их полный список вы можете найти в документации. Скажем, мы хотим напечатать имя файла в сообщении об ошибке. Значение fileName
, полученное при помощи функции getArgs
, напечатать нельзя, потому что в обработчик передаётся только значение типа IOException
и он не знает ни о чём другом. Функция зависит только от своих параметров. Но мы можем вызвать функцию ioeGetFileName
, которая по переданному ей исключению возвращает Maybe FilePath
. Функция пытается получить из значения исключения имя файла, если такое возможно. Давайте изменим обработчик так, чтобы он печатал полное имя файла, из-за которого возникло исключение (не забудьте включить функцию ioeGetFileName
в список импорта для модуля System.IO.Error
):
handler :: IOException -> IO ()
handler e
| isDoesNotExistError e =
case ioeGetFileName e of
Just fileName -> putStrLn $ "Файл " ++ fileName ++
" не существует!"
Nothing -> putStrLn "Файл не существует!"
| otherwise = ioError e
where fileName = ioeGetFileName e
В охранном выражении, если предикат isDoesNotExistError
вернёт значение True
, мы использовали выражение case
, чтобы вызвать функцию ioeGetFileName
с параметром e
; затем сделали сопоставление с образцом по возвращённому значению с типом Maybe
. Выражение case
часто используется в случаях, когда вам надо сделать сопоставление с образцом, не создавая новую функцию. Посмотрим, как это сработает:
$ ./linecount dont_exist.txt
Файл dont_exists.txt не существует!
Вы не обязаны использовать один обработчик для перехвата всех исключений в части кода, работающей с системой ввода-вывода. Вы можете перекрыть только отдельные части кода с помощью функции catch
или перекрывать разные участки кода разными обработчиками, например так:
main = do
action1 `catch` handler1
action2 `catch` handler2
launchRockets
Функция action1
использует функцию handler1
в качестве обработчика, а функция action2
использует handler2
. Функция launchRockets
не является параметром функции catch
, так что любое сгенерированное в ней исключение обрушит нашу программу, если только эта функция не использует try
или catch
внутри себя для обработки собственных ошибок. Конечно же, action1
, action2
и launchRockets
– это действия ввода-вывода, которые «склеены» друг с другом блоком do
и, вероятно, определены где-то в другом месте. Это похоже на блоки try–catch
в других языках: вы можете поместить всю вашу программу в один блок try–catch
или защищать отдельные участки программы и перехватывать различные исключения для разных участков.
Вспомогательные функции для работы с исключениями
Ранее в этой главе мы уже познакомились с функциями bracket
и bracketOnError
, которые реализуют наиболее часто используемый сценарий обработки исключений, когда работа с ресурсом состоит из трёх стадий:
• получение ресурса;
• использование ресурса;
• освобождение ресурса.
В наших примерах на первой стадии открывался файл, на второй шла работа с его содержимым, а на третьей файл закрывался. Функция bracket
гарантировала выполнение всех трёх действий, даже если в процессе генерировалось исключение, а функция bracketOnError
запускала третье действие только в случае возникновения исключения.
Обратите внимание, что программист, использующий такого рода функции, не работает непосредственно с исключениями – ему лишь достаточно понимать логику и порядок вызова конкретных действий.
Модуль Control.Exception
содержит ещё несколько подобных функций. Функция finally
обеспечивает гарантированное выполнение некоторого действия по завершении другого действия. Это всего навсего упрощённый вариант функции bracket
. Вот её сигнатура:
finally :: IO a -> IO b -> IO a
В следующем примере текст "Готово!"
печатается в каждом из двух случаев, несмотря на возникновение исключения во втором:
ghci> print (20 `div` 10) `finally` putStrLn "Готово!"
2
Готово!
ghci> print (2 `div` 0) `finally` putStrLn "Готово!"
Готово!
*** Exception: divide by zero
Функция onException
позволяет выполнить заключительное действие только в случае возникновения исключения:
ghci> print (20 `div` 10) `onException` putStrLn "Ошибка!"
2
ghci> print (2 `div` 0) `finally` putStrLn "Ошибка!"
Ошибка!
*** Exception: divide by zero
Заметьте, что обе эти функции, в отличие от try
или catch
, не обрабатывают исключения – они лишь гарантируют выполнение указанных действий. Все эти функции нетрудно реализовать вручную, пользуясь лишь try
или catch
. Фактически они устанавливают свой обработчик, перехватывают исключение, выполняют заданные действия, а после этого повторно генерируют то же самое исключение. Тем не менее, если ваша задача соответствует одному из приведённых сценариев, стоит воспользоваться уже существующей функцией.
10
Решение задач в функциональном стиле
В этой главе мы рассмотрим пару интересных задач и узнаем, как мыслить функционально для того, чтобы решить их по возможности элегантно. Скорее всего, мы не будем вводить новых концепций, а просто используем вновь приобретённые навыки работы с языком Haskell и попрактикуем методы программирования. Каждый раздел представляет отдельную задачу. Мы будем давать её описание и предложим поиск лучшего (или не самого худшего) решения.
Вычисление выражений в обратной польской записи
Обычно мы записываем математические выражения в инфиксной нотации, например: 10 – (4 + 3) * 2
. Здесь +
, *
и –
представляют собой инфиксные операторы, такие же, как инфиксные функции Haskell ( +
, `elem`
и т. д.). Так нам удобнее, потому что мы можем легко разобрать подобную формулу в уме. Но у такой записи есть и негативное свойство: приходится использовать скобки для обозначения приоритета операций.
Интервал:
Закладка: