Table Of ContentAlejandro Serrano Mena
Beginning Haskell:
A Project-Based
Approach
Алехандро Серано Мена
ИЗУЧАЕМ
HASKELL
А. Мена
Изучаем Haskell. Библиотека программиста
Серия «Библиотека программиста»
Перевел с английского Н. Вильчинский
Заведующий редакцией П. Щеголев
Ведущий редактор Ю. Сергиенко
Литературный редактор А. Жданов
Художник В. Шимкевич
Корректор С. Беляева, Н. Викторова
Верстка Л. Соловьева
ББК 32.973.2-018.1
УДК 004.43
Мена А.
М45 Изучаем Haskell. Библиотека программиста. — СПб.: Питер, 2015. — 464 с.:
ил. — (Серия «Библиотека программиста»).
ISBN 978-5-496-01188-4
Эта книга поможет вам быстро освоить базовые концепции языка программирования Haskell, его
библиотеки и компоненты, а также заложит основы функциональной парадигмы программирования, ко-
торая становится все более значимой в современном мире разработки ПО. Книга предлагает проектный
подход к освоению материала, используя в качестве прототипа проект реализации интернет-магазина.
Здесь рассматривается экосистема языка Haskell и его вспомогательных средств, инструменты Cabal
для управление проектами, модули HUnit и QuickCheck для тестирования программ, фреймворк Scotty
для разработки веб-приложений, Persistent и Esqueleto — для управления базами данных и многие
другие компоненты и библиотеки Haskell.
12+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)
ISBN 978-1430262503 англ. © Apress
ISBN 978-5-496-01188-4 © Перевод на русский язык ООО Издательство «Питер», 2015
© Издание на русском языке, оформление
ООО Издательство «Питер», 2015
Права на издание получены по соглашению с Apress. Все права защищены. Никакая часть данной книги не может
быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как на-
дежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может
гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные
ошибки, связанные с использованием книги.
ООО «Питер Пресс», 192102, Санкт-Петербург, ул. Андреевская (д. Волкова), д. 3, литер А, пом. 7Н.
Налоговая льгота — общероссийский классификатор продукции ОК 034-2014, 58.11.12.000 —
Книги печатные профессиональные, технические и научные.
Подписано в печать 28.10.14. Формат 70×100/16. Усл. п. л. 37,410. Тираж 1000. Заказ 0000.
Отпечатано в полном соответствии с качеством предоставленных издательством материалов
в ГППО «Псковская областная типография». 180004, Псков, ул. Ротная, 34.
Оглавление
Об авторе . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
О научном редакторе . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Благодарности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Введение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Зачем изучать функциональное программирование . . . . . . . . . . . . . 11
Почему вам нужна эта книга . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Часть I . Первые шаги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Глава 1 . Настоящий функциональный язык . . . . . . . . . . . . . . . . . 14
Почему именно Haskell? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
История Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Ваша рабочая среда . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Первые шаги в использовании GHCi . . . . . . . . . . . . . . . . . . . . . . . . . 24
Магазин, продающий машины времени . . . . . . . . . . . . . . . . . . . . . . 26
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Глава 2 . Объявление модели данных . . . . . . . . . . . . . . . . . . . . . . . 28
Работа с символами, числами и списками . . . . . . . . . . . . . . . . . . . . . 28
Создание нового проекта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Определение простых функций . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Работа с типами данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Сопоставление с образцом . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Записи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Глава 3 . Многократное использование кода
с помощью списков . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Параметрический полиморфизм . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Функции в качестве аргументов . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Подробнее о модулях . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Углубленное изучение списков . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Формирователи списков . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6 Оглавление
Haskell-оригами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Глава 4 . Контейнеры и классы типов . . . . . . . . . . . . . . . . . . . . . . 100
Использование пакетов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Контейнеры: проекции, множества, деревья и графы . . . . . . . . . . . 108
Специальный полиморфизм: классы типов . . . . . . . . . . . . . . . . . . . 120
Двоичные деревья для минимальной цены . . . . . . . . . . . . . . . . . . . 129
Классы типов, связанные с контейнерами . . . . . . . . . . . . . . . . . . . 134
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Глава 5 . Лень и бесконечные структуры . . . . . . . . . . . . . . . . . . . 139
Бесконечное количество машин времени . . . . . . . . . . . . . . . . . . . . 139
Модель ленивых вычислений . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Профилирование с помощью GHC . . . . . . . . . . . . . . . . . . . . . . . . . 152
Строгие аннотации . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
Часть II . Анализ данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Глава 6 . Ознакомление с клиентами с помощью монад . . . . . . . 162
Анализ данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Исследование монад . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Различные виды состояний . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Глава 7 . Другие рекомендуемые монады . . . . . . . . . . . . . . . . . . . 194
Возвращение более одного значения . . . . . . . . . . . . . . . . . . . . . . . 194
Неудачи и альтернативы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Вывод ассоциативных правил . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Задачи поиска . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Монада Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Возрождение монад и списков . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Глава 8 . Использование нескольких ядер . . . . . . . . . . . . . . . . . . 224
Параллелизм, одновременность, распределенность . . . . . . . . . . . . 224
Монада Par . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Программная транзакционная память . . . . . . . . . . . . . . . . . . . . . . . 232
Облачные решения для Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Оглавление 7
Часть III . Использование ресурсов . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Глава 9 . Работа с файлами: ввод-вывод и библиотека conduit 250
Базовые ввод и вывод . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
Случайность . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Работа с файлами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Обработка ошибок . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Потоковый ввод-вывод данных с помощью библиотеки conduit . . . . 270
За пределами текстовых файлов . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Глава 10 . Создание и синтаксический разбор текста . . . . . . . . . 282
Пять текстовых типов данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
Создание текста со скоростью ветра . . . . . . . . . . . . . . . . . . . . . . . 287
Синтаксический разбор с использованием пакета attoparsec . . . . . . 290
Представление новых классов типов . . . . . . . . . . . . . . . . . . . . . . . 296
Не нужно излишеств: используйте формат JSON . . . . . . . . . . . . . . 304
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
Глава 11 . Безопасный доступ к базам данных . . . . . . . . . . . . . . . 311
Средства доступа к базам данных . . . . . . . . . . . . . . . . . . . . . . . . . 311
Соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Схемы и переносы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Запросы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Вставка, обновление и удаление . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
Глава 12 . Веб-приложения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Веб-экосистема Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Структура RESTful . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Разработка внутреннего интерфейса в среде Scotty . . . . . . . . . . . . 339
Разработка внешнего интерфейса с помощью компилятора Fay . . . 349
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Часть IV . Предметно-ориентированные языки . . . . . . . . . . . . . . . . . 355
Глава 13 . Строгие типы для описания предложений . . . . . . . . . 356
Предметно-ориентированные языки . . . . . . . . . . . . . . . . . . . . . . . . 356
Безопасность языка выражений . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Зависимая типизация . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
Программирование на уровне типов в Haskell . . . . . . . . . . . . . . . . . 370
Функциональные зависимости . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
8 Оглавление
Семейства типов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
Продвижение типа и одноэлементные типы . . . . . . . . . . . . . . . . . . 385
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
Глава 14 . Интерпретация предложений с помощью атрибутов 395
Варианты интерпретации и атрибутивные грамматики . . . . . . . . . . 395
Наша первая атрибутивная грамматика . . . . . . . . . . . . . . . . . . . . . 398
Интеграция UUAGC-кода в пакет . . . . . . . . . . . . . . . . . . . . . . . . . . 401
Интерпретация выражений . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
Варианты интерпретации предложений . . . . . . . . . . . . . . . . . . . . . 409
Оригами-программирование для любого типа данных . . . . . . . . . . 415
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Часть V . Инжиниринг магазина . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
Глава 15 . Документирование, тестирование и проверка . . . . . . 420
Документирование двоичных деревьев с помощью
утилиты Haddock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
Блочное тестирование с помощью утилиты HUnit . . . . . . . . . . . . . . 424
Рандомизированное тестирование с помощью библиотеки
QuickCheck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
Формальная верификация с использованием языка Idris . . . . . . . . . 434
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
Глава 16 . Создание архитектуры приложения . . . . . . . . . . . . . . 439
Паттерны проектирования и функциональное программирование . . 439
Рекомендации среднего порядка . . . . . . . . . . . . . . . . . . . . . . . . . . 441
Утилиты . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
Проекты . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
Выводы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
Приложение А . Дальнейшие перспективы . . . . . . . . . . . . . . . . . . 459
Haskell-ресурсы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
Приложение Б . Путешествие во времени с Haskell . . . . . . . . . . . 462
Об авторе
Алехандро Серано Мена — горячий сторонник функционального программирова-
ния, он использует Haskell для личных и коммерческих проектов уже более пяти
лет. В настоящее время пишет кандидатскую диссертацию в Утрехтском универ-
ситете. Область его интересов связана с разработкой систем со строгой типизацией
и взаимодействием с такими системами, например, посредством языка Haskell.
Он внес вклад в ряд проектов с открытым кодом, включая Mono и Nemerle.
В 2011 году принял участие в программе Google Summer of Code, совершенствуя
подключаемый Haskell-модуль EclipseFP для популярной среды разработки
Eclipse. В 2012 году он с двумя друзьями основал компанию Nublic, деятельность
которой сконцентрирована на предоставлении облачных инструментов для до-
машних сред. Основная часть программного обеспечения данного проекта была
создана с использованием языка Scala, сочетающего объектно-ориентированные
и функциональные аспекты.
Диплом по информатике и математике Алехандро получил в родном Мадридском
университете. В период обучения он активно продвигал среди студентов идеи
свободного программного обеспечения и функциональной парадигмы. Его статьи
можно прочитать в испанском журнале «Todo Programación».
О научном редакторе
Жан-Филипп Моресмо пишет программы уже пятнадцать лет. Он занимался
созданием веб-приложений с 1996 года и всегда испытывал интерес к интегриро-
ванным средам разработки и анализу языка.
Он принимает активное участие в работе сообщества по разработке приложений
с открытым кодом на языке Haskell, а также входит в группу поддержки проекта
EclipseFP. Его блог находится по адресу http://jpmoresmau .blogspot .com. Ж.-Ф. Мо-
ресмо с женой и двумя детьми живет на юге Франции.
Благодарности
Прежде всего, хотел бы отметить грандиозную работу научного редактора и ре-
дакции. Их комментарии и предложения представляли исключительную ценность
и позволили существенно улучшить качество книги. Еще я хотел бы отдельно от-
метить великолепную работу Жана-Филиппа Моресмо над EclipseFP.
Написание книги — дело не только благодарное, но и хлопотное. И в те моменты,
когда настроение падало до нуля, я всегда ощущал поддержку Елены. Она была
со мной и в минуты размышлений, и в минуты оттачивания примеров, и в минуты
бесконечных правок текста.
Cемья и друзья поддерживали меня все это время, поэтому большое спасибо вам
всем. Мои родители, Кармен и Хулиан, заслуживают особого упоминания: они
поддерживали меня при выполнении каждого моего проекта, и как бы странно это
ни звучало, на протяжении всей моей жизни. Они купили мне первый компьютер,
и именно на нем я впервые начал программировать в старой доброй среде Visual
Basic. Поэтому хотя бы ради справедливости нужно сказать, что без их помощи вы
бы никогда не читали эту книгу.
Нельзя не восхититься всем Haskell-сообществом. Наличие обширных подписок
на рассылки и групповых чатов, наполненных собеседников (заметьте, умных
собеседников!), всегда готовых прийти на помощь, является хорошим стимулом
для проявления любопытства и познания нового. Каждая библиотека и каждый
компилятор, о которых рассказывается в книге, были с любовью разработаны этим
сообществом: именно они сделали Haskell таким великим языком.
И в завершение хочу поблагодарить своих преподавателей и коллег из Утрехтско-
го университета. Хотя мы были знакомы всего лишь пару месяцев, их страстное
увлечение функциональным программированием оказало существенное влияние
на книгу.