public/private module sections (was Re[2]: Export lists in modules)

Claus Reinke claus.reinke at
Thu Feb 23 20:18:35 EST 2006

let's go through 5.2 "Export Lists" to see what would be missing 
if we tried to replace the export list with a separation of a module
into a public (exported) and a private (local) part:

    module M 

1. "value, field name, or class method, whether declared in the 
    module body or imported, may be named by giving the name 
    of the value as a qvarid"

    the easiest way to do that is to put the definition of that name
    or a synonym for it in the export section. to reexport names
    from other modules, import those names in the export section.
    that approach gets awkward if we only want to export some 
    field names of a data type.
    [ISSUE 1]

2. algebraic datatype T declared by a data or newtype declaration 
    - The form T names the type but not the constructors or 
        field names. 
      declare T itself in the local section, declare a type synonym 
        for T in the export section

    - The form T(c1,...,cn), names the type and some or all of its
        constructors and field names. 

        declare T in the local section, and synonyms for T and for 
        some of its fields/constructors in the export section. 

        again, the latter is awkward. worse, it doesn't work when
        constructors are used in patterns. 
        [ISSUE 2]

    - The abbreviated form T(..) names the type and all its 
        constructors and field names 

        declare T in the export section

3. A type synonym T declared by a type declaration 

    declare T in the export section

4. A class C with operations f1,...,fn declared in a class declaration
    - The form C names the class but not the class methods. 

        declare C in the local section. declare a synonym for C in
        the export section. 

        (it is strange that Haskell 98 allows us to export a class 
        without its methods, yet without restricting its use; 
        instantiating such a class would only make sense if all 
        hidden methods had default definitions, wouldn't it?
        so perhaps the class synonym would only need to be
        one-sided: for use, not for adding instances?).

    - The form C(f1,...,fn), names the class and some or all of its methods. 

        declare C in the local section, declare a partial synonym for 
        C, including some of its methods, in the export section. 

        (again, it doesn't seem to make much sense to make that 
        more than a one-sided synonym; see previous point).

    - The abbreviated form C(..) names the class and all its methods

        declare C in the export section

5. The form "module M" names the set of all entities that are in scope 
    with both an unqualified name "e" and a qualified name "M.e". 

    for re-exports M, import M in the export section.
    for the current module: ??
    the current module seems to need syntax at first, until we realize that 
    this case is already handled by the split into export/local section.

    for imports we don't want to re-export, import them in the local 
    section (so imports need to be allowed in two places, at the 
    beginning of the exported section and at the beginning of the 
    local section (but that seems to be no problem, more relaxed 
    versions of import have been suggested here).

    note that it is no longer possible to export a module M that
    has not been imported. so the description of that error in the 
    report can be removed

so far, so good, if we can resolve the issues mentioned above,
there is a lot of simplicifation in return: 

    - no export lists
        (so the language definition becomes simpler, and if some 
        future standard tackles module interfaces, they won't have 
        to compete with overlaps between export lists and interfaces)

    - no need to duplicate features of import lists in export lists, 
        as import lists in export sections serve the same purpose 

    - less potential for errors

but that's not the end of the advantages: compared to other
proposals on the table, there is no duplication of type signatures,
nor of export information, and whether or not an item is
exported is directly visible from its presence in either section.

6. (cf. 5.4 "importing and exporting instances")

    to export an instance, declare it in the export section.
    to avoid exporting an instance, declare it in the local section.
    to import instances for re-export, import them in the export
    to import instances *without re-exporting* them, import
    them in the local section! (hooray!-)

    this is not a perfect success, however: we have selective
    export of instances, but not selective import - we have no 
    chance to import names from a module M without importing 
    its exported instances as well.
    [ISSUE 3]

the more I think about it, the more I like it (which probably
means that there is some ugly part of it that I'm not thinking
about;-). the outstanding issues seem to be:

ISSUE 1 (cosmetic/annoying): 

    exporting a subset of field names via individual synonyms

ISSUE 2 (critical):

    exporting a subset of data constructors (synonyms only
    work for construction, not for pattern-matching). 

    happily, there has already been a suggestion to introduce 
    pattern synonyms, which would fit the bill precisely. note
    that this allows us to control separately the export of pattern
    and constructor aspects, which has been requested anyway.

    [btw, does anyone export only a subset of data constructors
     for a data type? I'm curious about uses of this feature.]

ISSUE 3 (less an issue than with the current system, actually):

    selective import of instances (we do have selective 
    export, which is better than the current system, but not 
    quite sufficient).

    referring to the proposal for annotating exports with
    type or class modifiers, that approach would work here
    as well. all we need is to allow an anonymous modifier 
    "instances" in import lists (*)

    import M (instances, class C, type T) -- import instances
    import M (class C, type T) -- do not import instances
    import M -- import instances
    import M() -- do not import instances

any other issues I missed here? the translation from old to
new system seems completely mechanical, and if the
old system offers a feature subset of the new system, the
old system could be kept, for one iteration of the language
definition, as deprecated syntactic sugar for the new one
(if immediate backwards compatibility would seem more
important than immediate simplification of the language 
definition and automatic translation of old programs).


(*) actually, that would work independently of the rest 
        of this message, with the current system, if applied
        to both export and import lists.

More information about the Haskell-prime mailing list