> module ModSysAST where > > import NamesEntities > import Relations Abstract Syntax =============== \label{AbsSyn} As described in the Haskell 98 report \cite[Section 5.1]{Haskell98}, a Haskell module consists of a name, an export specification, a number of import declarations and a number of local definitions. We use the following data structure to represent modules: > data Module = Module { > modName :: ModName, > modExpList :: Maybe [ExpListEntry], > modImports :: [Import], > modDefines :: Rel Name Entity } The concrete syntax of Haskell allows an abbreviated form, where the module name and the export specification are omitted. This is an abbreviation for a module with name ``Main'' and an export specification exporting a single entity named ``main'' \cite[Section 5.1]{Haskell98} and will in our abstract syntax be represented in its expanded form. An element of the export specification is either an entity name or a module name as described by the data structure #ExpListEntry#. For entities with subordinate names, a programmer may also provide a _subordinate export list_. This list is modeled by the data structure #SubSpec#. It specifies which of the subordinate entities currently in scope are to be exported. > data ExpListEntry = EntExp (EntSpec QName) > | ModuleExp ModName > data EntSpec j = Ent j (Maybe SubSpec) > data SubSpec = AllSubs | Subs [Name] \begin{ex} For the Haskell module: | module A (f,C(..),module M) where ... the field #modExpList# would be: | Just [EntExp (Ent "f" Nothing), | EntExp (Ent "C" (Just AllSubs)), | ModuleExp "M"] \end{ex} The structure #EntSpec# is used in both import and export lists. Because qualified names are allowed in export lists, but not in import lists, we use the parameter #j# to capture the different types of #EntSpec#. At first it may seem that we may eliminate #AllSubs# by thinking of it as just an abbreviation for all the methods/value constructors of its owner. This however is not the case, as its meaning depends on what entities are currently in scope, and this is one of the things the module system computes. The lack of an export list is a special form of export specification: one saying that only---and all---locally defined entities are to be exported \cite[Section 5.2]{Haskell98}. It is _not_ an abbreviation for the empty export list, or the export list containing only #module M# (where #M# is the current module). We represent this explicitly by using the #Maybe# type constructor in the #modExpList# field. To make use of entities defined in other modules, programmers have to supply _import declarations_. Their purpose it to specify what entities are to be imported, which module provides the required entities, and valid ways to refer to the imported entities. > data Import = Import { > impQualified :: Bool, > impSource :: ModName, > impAs :: ModName, > impHiding :: Bool, > impList :: [EntSpec Name] } The #impSource# field is the only field that must be specified explicitly in an import specification. It specifies the name of the module from which entities will be imported. All remaining fields take on a default value, if not specified explicitly. There are two flavors of import declarations: the ones specifying what names are to be imported, and the ones specifying what names are _not_ to be imported (sometimes called ``hiding'' imports). The boolean field #impHiding# distinguishes between those two. The field #impList# contains the actual specification, which has structure similar to the export list of a module. There are two differences: there are no ``module'' imports, and all names in the list must be simple. To capture this similarity we reuse the #EntSpec# data type. If this field is omitted the specification is assumed to be #[]#, and the #impHiding# field is set to #True#. This has the effect of importing all exported entities of the source module. Sometimes it is more convenient to qualify names imported from a module not using the module name, but some other alias instead. This is particularly useful if the name of the source module is quite long and a programmer needs to refer often to imported entities by their qualified names. The field #impAs# stores this alias. If the alias is omitted, this field is assumed to have the same value as the #impFrom# fields (i.e. we use the module name in qualified names). Finally in some situations it might be preferable to only import entities with their qualified names. This can be done with the so called _qualified_ imports. The field #impQualified# distinguishes qualified from normal imports. \begin{ex} The import: | import Prelude as P hiding (and,Bool(True)) is represented by the data structure: | Import {impQualified = False, | impSource = "Prelude", | impAs = "P", | impHiding = True, | impList = [Ent "and" Nothing, | Ent "Bool" (Just (Subs ["True"]))] | } \end{ex}