Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Иди налево, потом направо
Ещё один чудесный тип, принимающий два других в качестве параметров, – это тип Either
. Он определён приблизительно так:
data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
У него два конструктора данных. Если используется конструктор Left
, его содержимое имеет тип a
; если Right
– содержимое имеет тип b
. Таким образом, мы можем использовать данный тип для инкапсуляции значения одного из двух типов. Когда мы работаем с типом Either a b
, то обычно используем сопоставление с образцом по Left
и Right
и выполняем действия в зависимости от того, какой вариант совпал.
ghci> Right 20
Right 20
ghci> Left "в00т"
Left "в00т"
ghci> :t Right 'a'
Right 'a' :: Either a Char ghci> :t Left True
Left True :: Either Bool b
Из приведённого примера следует, что типом значения Left
True
является Either
Bool
b
. Первый параметр типа Bool
, поскольку значение создано конструктором Left
; второй же параметр остался полиморфным. Ситуация подобна тому как значение Nothing
имеет тип Maybe
a
.
Мы видели, что тип Maybe
главным образом используется для того, чтобы представить результат вычисления, которое может завершиться неудачей. Но иногда тип Maybe
не так удобен, поскольку значение Nothing
не несёт никакой информации, кроме того что что-то пошло не так. Это нормально для функций, которые могут выдавать ошибку только в одном случае – или если нам просто не интересно, как и почему функция «упала». Поиск в отображении типа Data.Map
может завершиться неудачей, только если искомый ключ не найден, так что мы знаем, что случилось. Но если нам нужно знать, почему не сработала некоторая функция, обычно мы возвращаем результат типа Either a b
, где a
– это некоторый тип, который может нам что-нибудь рассказать о причине ошибки, и b
– результат удачного вычисления. Следовательно, ошибки используют конструктор данных Left
, правильные результаты используют конструктор Right
.
Например, в школе есть шкафчики для того, чтобы ученикам было куда клеить постеры Guns’n’Roses. Каждый шкафчик открывается кодовой комбинацией. Если школьнику понадобился шкафчик, он говорит администратору, шкафчик под каким номером ему нравится, и администратор выдаёт ему код. Если этот шкафчик уже кем-либо используется, администратор не сообщает код – они вместе с учеником должны будут выбрать другой вариант. Будем использовать модуль Data.Map
для того, чтобы хранить информацию о шкафчиках. Это будет отображение из номера шкафчика в пару, где первый компонент указывает, используется шкафчик или нет, а второй компонент – код шкафчика.
import qualified Data.Map as Map
data LockerState = Taken | Free deriving (Show, Eq)
type Code = String
type LockerMap = Map.Map Int (LockerState, Code)
Довольно просто. Мы объявляем новый тип данных для хранения информации о том, был шкафчик занят или нет. Также мы создаём синоним для кода шкафчика и для типа, который отображает целые числа в пары из статуса шкафчика и кода. Теперь создадим функцию для поиска кода по номеру. Мы будем использовать тип Either String Code
для представления результата, так как поиск может не удаться по двум причинам – шкафчик уже занят, в этом случае нельзя сообщать код, или номер шкафчика не найден вообще. Если поиск не удался, возвращаем значение типа String
с пояснениями.
lockerLookup :: Int –> LockerMap –> Either String Code
lockerLookup lockerNumber map =
case Map.lookup lockerNumber map of
Nothing –> Left $ "Шкафчик № " ++ show lockerNumber ++
" не существует!"
Just (state, code) –>
if state /= Taken
then Right code
else Left $ "Шкафчик № " ++ show lockerNumber ++ " уже занят!"
Мы делаем обычный поиск по отображению. Если мы получили значение Nothing
, то вернём значение типа Left String
, говорящее, что такой номер не существует. Если мы нашли номер, делаем дополнительную проверку, занят ли шкафчик. Если он занят, возвращаем значение Left
, говорящее, что шкафчик занят. Если он не занят, возвращаем значение типа Right Code
, в котором даём студенту код шкафчика. На самом деле это Right String
, но мы создали синоним типа, чтобы сделать наши объявления более понятными. Вот пример отображения:
lockers :: LockerMap lockers = Map.fromList
[(100,(Taken,"ZD39I"))
,(101,(Free,"JAH3I"))
,(103,(Free,"IQSA9"))
,(105,(Free,"QOTSA"))
,(109,(Taken,"893JJ"))
,(110,(Taken,"99292"))
]
Давайте попытаемся узнать несколько кодов.
ghci> lockerLookup 101 lockers
Right "JAH3I"
ghci> lockerLookup 100 lockers
Left "Шкафчик № 100 уже занят!"
ghci> lockerLookup 102 lockers
Left "Шкафчик № 102 не существует!"
ghci> lockerLookup 110 lockers
Left "Шкафчик № 110 уже занят!"
ghci> lockerLookup 105 lockers
Right "QOTSA"
Мы могли бы использовать тип Maybe
для представления результата, но тогда лишились бы возможности узнать, почему нельзя получить код. А в нашей функции причина ошибки выводится из результирующего типа.
Рекурсивные структуры данных

Как мы уже видели, конструкторы алгебраических типов данных могут иметь несколько полей (или не иметь вовсе), и у каждого поля должен быть конкретный тип. Принимая это во внимание, мы можем создать тип, конструктор которого имеет поля того же самого типа! Таким образом мы можем создавать рекурсивные типы данных, где одно значение некоторого типа содержит другие значения этого типа, а они, в свою очередь, содержат ещё значения того же типа, и т. д.
Посмотрите на этот список: [5]
. Это упрощённая запись выражения 5:[]
. С левой стороны от оператора :
ставится значение, с правой стороны – список (в нашем случае пустой). Как насчёт списка [4,5]
? Его можно переписать так: 4:(5:[])
. Смотря на первый оператор :
, мы видим, что слева от него – всё так же значение, а справа – список (5:[])
. То же можно сказать и в отношении списка 3:(4:(5:6:[]))
; это выражение можно переписать и как 3:4:5:6:[]
(поскольку оператор :
правоассоциативен), и как [3,4,5,6]
.
Мы можем сказать, что список может быть пустым или это может быть элемент, присоединённый с помощью оператора :
к другому списку (который в свою очередь может быть пустым или нет).
Ну что ж, давайте используем алгебраические типы данных, чтобы создать наш собственный список.
data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
Это можно прочитать почти как наше определение списка в одном из предыдущих разделов. Это либо пустой список, либо комбинация некоторого значения («головы») и собственно списка («хвоста»). Если такая формулировка трудна для понимания, то с использованием синтаксиса записей она будет восприниматься легче.
Читать дальшеИнтервал:
Закладка: