Стандартная библиотека языка Haskell разбита на модули, каждый из которых содержит взаимосвязанные функции и типы, служащие некоторой общей цели. Есть модуль для работы со списками, модуль для параллельного программирования, модуль для работы с комплексными числами и т. д. Все функции, типы и классы типов, с которыми мы имели дело до сих пор, были частью стандартного модуля Prelude
– он импортируется по умолчанию. В этой главе мы познакомимся с несколькими полезными модулями и их функциями. Но для начала посмотрим, как импортировать модули.
Импорт модулей
Синтаксис для импорта модулей в программах на языке Haskell – import
. Импортировать модули надо прежде, чем вы приступите к определению функций, поэтому обычно импорт делается в начале файла. Конечно же, одна программа может импортировать несколько модулей. Для этого вынесите каждый оператор import
в отдельную строку.
Давайте импортируем модуль Data.List
, который содержит массу функций для работы со списками, и используем экспортируемую им функцию для того, чтобы написать свою – вычисляющую, сколько уникальных элементов содержит список.
import Data.List
numUniques :: (Eq a) => [a] –> Int
numUniques = length . nub
Когда выполняется инструкция import Data.List
, все функции, экспортируемые модулем Data.List
, становятся доступными в глобальном пространстве имён. Это означает, что вы можете вызывать их из любого места программы. Функция nub
определена в модуле Data.List
; она принимает список и возвращает список, из которого удалены дубликаты элементов исходного списка. Композиция функций length
и nub
создаёт функцию, которая эквивалентна \xs –> length (nub xs)
.
ПРИМЕЧАНИЕ. Чтобы найти нужные функции и уточнить, где они определены, воспользуйтесь сервисом Hoogle, который доступен по адресу http://www.haskell.org/hoogle/. Это поистине удивительный поисковый механизм для языка Haskell, который позволяет вести поиск по имени функции, по имени модуля и даже по сигнатуре.
В интерпретаторе GHCi вы также можете подключить функции из модулей к глобальному пространству имён. Если вы работаете в GHCi и хотите вызывать функции, экспортируемые модулем Data.List
, напишите следующее:
ghci> :m + Data.List
Если требуется подгрузить программные сущности из нескольких модулей, не надо вызывать команду :m +
несколько раз, так как можно загрузить ряд модулей одновременно:
ghci> :m + Data.List Data.Map Data.Set
Кроме того, если вы загрузили скрипт, который импортирует модули, то не нужно использовать команду :m +
, чтобы получить к ним доступ.
Если вам необходимо всего несколько функций из модуля, вы можете выборочно импортировать только эти функции. Если бы вам были нужны только функции nub
и sort
из модуля Data.List
, импорт выглядел бы так:
import Data.List (nub, sort)
Также вы можете осуществить импорт всех функций из модуля за исключением некоторых. Это бывает полезно, когда несколько модулей экспортируют функции с одинаковыми именами, и вы хотите избавиться от ненужных повторов. Предположим, у вас уже есть функция с именем nub
и вы хотите импортировать все функции из модуля Data.List
, кроме nub
, определённой в нём:
import Data.List hiding (nub)
Другой способ разрешения конфликтов имён – квалифицированный импорт. Модуль Data.Map
, который содержит структуру данных для поиска значения по ключу, экспортирует несколько функций с теми же именами, что и модуль Prelude
, например filter
и null
. Если мы импортируем модуль Data.Map
и вызовем функцию filter
, язык Haskell не будет знать, какую функцию использовать. Вот как можно обойти такую ситуацию:
import qualified Data.Map
Если после такого импорта нам понадобится функция filter
из модуля Data.Map
; мы должны вызывать её как Data.Map.filter
– просто идентификатор filter
ссылается на обычную функцию из модуля Prelude
, которую мы все знаем и любим. Но печатать строку Data.Map
перед именем каждой функции может и поднадоесть! Вот почему желательно переименовать модуль при импорте во что-нибудь более короткое:
import qualified Data.Map as M
Теперь, чтобы сослаться на функцию из Data.Map
, мы вызываем её как M.filter
.