В Haskell’е модули несут двоякое назначение — с одной стороны модули необходимы для контроля над пространством имён (как, собственно, и во всех других языках программирования), с другой стороны при помощи модулей можно создавать абстрактные типы данных.
Определение модуля в Haskell’е достаточно просто. Именем модуля может быть любой символ, начинается имя только с заглавной буквы. Дополнительно имя модуля никак не связано с файловой системой (как, например, в Pascal’е и в Java), т.е. имя файла, содержащего модуль, может быть не таким же, как и название модуля. На самом деле, в одном файле может быть несколько модулей, т.к. модуль — это всего лишь декларация самого высокого уровня.
Как известно, на верхнем уровне модуля в Haskell’е может быть множество деклараций (описаний и определений) — типы, классы, данные, функции. Однако один вид деклараций должен стоять в модуле на первом месте (если этот вид деклараций вообще используется). Речь идет о включении в модуль других модулей — для этого используется служебное слово import. Остальные определения могут появляться в любой последовательности.
Определение модуля должно начинаться со служебного слова module. Например, ниже приведено определение модуля Tree:
module Tree (Tree (Leaf, Branch), fringe) where
data Tree a = Leaf a
| Branch (Tree a) (Tree a)
fringe :: Tree a -> [a]
fringe (Leaf x) = [x]
fringe (Branch left right) = fringe left ++ fringe right
В этом модуле описан один тип (Tree — ничего страшного, что имя типа совпадает с названием модуля, в данном случае они находятся в различных пространствах имён) и одна функция (fringe). В данном случае модуль Tree явно экспортирует тип Tree (вместе со своими подтипами Leaf и Branch) и функцию fringe — для этого имена типа и функции указаны в скобках после имени модуля. Если наименование какого-либо объекта не указывать в скобках, то он не будет экспортироваться, т.е. этот объект не будет виден извне текущего модуля.
Для корректного отображения нового типа данных необходимо определить для него функцию Show. Наиболее простой способ:
data Tree a = Leaf a
| Branch (Tree a) (Tree a) deriving Show
Использование модуля в другом модуле выглядит еще проще:
module Main where
import Tree (Tree(Leaf, Branch), fringe)
main = print (fringe (Branch (Leaf 1) (Leaf 2)))
В приведенном примере видно, что модуль Main импортирует модуль Tree, причём в декларации import явно описано, какие именно объекты импортируются из модуля Tree. Если это описание опустить, то импортироваться будут все объекты, которые модуль экспортирует, т.е. в данном случае можно было просто написать: import Tree.
Бывает так, что один модуль импортирует несколько других (надо заметить, что это обычная ситуация), но при этом в импортируемых модулях существуют объекты с одним и тем же именем. Естественно, что в этом случае возникает конфликт имён. Чтобы этого избежать в Haskell’е существует специальное служебное слово qualified, при помощи которого определяются те импортируемые модули, имена объектов в которых приобретают вид: <Имя Модуля>.<Имя Объекта>, т.е. для того, чтобы обратиться к объекту из квалифицированного модуля, перед его именем необходимо написать имя модуля:
module Main where
import qualified Tree
main = print (Tree.fringe (Tree.Leaf ’a’))
Использование такого синтаксиса полностью лежит на совести программиста. Некоторым нравится полная определённость, которую привносят квалифицированные имена, и они используют их в ущерб размеру программ. Другим нравится использовать короткие мнемонические имена, и они используют квалификаторы (имена модулей) только по необходимости.