ghci> show Wednesday

"Wednesday"

ghci> read "Saturday" :: Day

Saturday

Поскольку он имеет экземпляры классов Eq и Ord, допускаются сравнение и проверка на равенство:

ghci> Saturday == Sunday

False

ghci> Saturday == Saturday

True

ghci> Saturday > Friday

True

ghci> Monday `compare` Wednesday

LT

Наш тип также имеет экземпляр класса Bounded, так что мы можем найти минимальный и максимальный день.

ghci> minBound :: Day

Monday

ghci> maxBound :: Day

Sunday

Благодаря тому что тип имеет экземпляр класса Enum, можно получать предшествующие и следующие дни, а также задавать диапазоны дней.

ghci> succ Monday

Tuesday

ghci> pred Saturday

Friday

ghci> [Thursday .. Sunday]

[Thursday,Friday,Saturday,Sunday]

ghci> [minBound .. maxBound] :: [Day]

[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]

Замечательно!

<p>Синонимы типов</p>

Ранее мы упоминали, что типы [Char] и String являются эквивалентами и могут взаимно заменяться. Это осуществляется с помощью синонимов типов. Синоним типа сам по себе ничего не делает – он просто даёт другое имя существующему типу, облегчая понимание нашего кода и документации. Вот так стандартная библиотека определяет тип String как синоним для [Char]:

type String = [Char]

Ключевое слово type может ввести в заблуждение, потому что на самом деле мы не создаём ничего нового (создаём мы с помощью ключевого слова data), а просто определяем синоним для уже существующего типа.

Если мы создадим функцию, которая преобразует строку в верхний регистр, и назовём её toUpperString, то можем дать ей сигнатуру типа toUpperString :: [Char] –> [Char] или toUpperString :: String –> String. Обе сигнатуры обозначают одно и то же, но вторая легче читается.

<p>Улучшенная телефонная книга</p>

Когда мы работали с модулем Data.Map, то вначале представляли записную книжку в виде ассоциативного списка, а потом преобразовывали его в отображение. Как мы уже знаем, ассоциативный список – это список пар «ключ–значение». Давайте взглянем на этот вариант записной книжки:

phoneBook :: [(String,String)]

phoneBook =

  [("оля","555–29-38")

  ,("женя","452–29-28")

  ,("катя","493–29-28")

  ,("маша","205–29-28")

  ,("надя","939–82-82")

  ,("юля","853–24-92")

  ]

Мы видим, что функция phoneBook имеет тип [(String,String)]. Это говорит о том, что перед нами ассоциативный список, который отображает строки в строки, – но не более. Давайте зададим синоним типа, и мы сможем узнать немного больше по декларации типа:

type PhoneBook = [(String,String)]

Теперь декларация типа для нашей записной книжки может быть такой: phoneBook :: PhoneBook. Зададим также синоним для String.

type PhoneNumber = String

type Name = String

type PhoneBook = [(Name,PhoneNumber)]

Те, кто программирует на языке Haskell, дают синонимы типу String, если хотят сделать объявления более «говорящими» – пояснить, чем являются строки и как они должны использоваться.

Итак, реализуя функцию, которая принимает имя и номер телефона и проверяет, есть ли такая комбинация в нашей записной книжке, мы можем дать ей красивую и понятную декларацию типа:

inPhoneBook :: Name –> PhoneNumber –> PhoneBook –> Bool

inPhoneBook name pnumber pbook = (name,pnumber) `elem` pbook

Если бы мы не использовали синонимы типов, тип нашей функции был бы String –> String –> [(String,String)] –> Bool. В этом случае декларацию функции легче понять при помощи синонимов типов. Однако не надо перегибать палку. Мы применяем синонимы типов для того, чтобы описать, как используются существующие типы в наших функциях (таким образом декларации типов лучше документированы), или когда мы имеем дело с длинной декларацией типа, которую приходится часто повторять (вроде [(String,String)]), причём эта декларация обозначает что-то более специфичное в контексте наших функций.

<p>Параметризация синонимов</p>
Перейти на страницу:

Похожие книги