Changes between Version 4 and Version 5 of GhcPackagesWithGrafting

Jul 7, 2006 9:43:12 AM (13 years ago)

moved to GhcPackageNamespaces


  • GhcPackagesWithGrafting

    v4 v5  
    1 [[PageOutline]]
    22== Alternative Proposal for Packages (with grafting) ==
    4 GhcPackageNamespaces
     4Moved to GhcPackageNamespaces to avoid the misleading title "Grafting" which is only incidental to the proposal.
    6 This proposal is an alternative to GhcPackages.  Large parts overlap with that
    7 proposal.  To motivate this new proposal, let's consider another
    8 proposed and desirable feature of the import/export language, which may
    9 interact in interesting ways with packages.
    11 == A different, but related, problem ==
    13 A problem that has been mentioned several times on mailing lists, is
    14 grafting part of a directory hierarchy into an arbitrary location
    15 elsewhere in the hierarchy.
    16 (See [])
    18 Another way of expressing a similar wish is the ability to re-export
    19 imports with a different qualified name, as in the scenario suggested by
    20 the developers of the package gtk2hs:
    21 []
    23 There are several desires in play here:
    25  * a desire to minimise typing of long qualified names
    26  * a desire to refer to "leaf" nodes of the hierarchy in a way that makes it easy to relocate those modules in the hierarchy, without needing to edit every import declaration that uses them
    27  * a desire to partially-qualify names for disambiguation
    29 == Proposal ==
    31 We introduce the new concept of ''namespace'' as something that can be
    32 declared in source code.  A namespace can contain only module names.
    33 (The specification of what module names are contained in a namespace is
    34 rather like our current concept of a package, i.e. not declared in the
    35 source code, but rather by some external mechanism e.g. grouping of
    36 files in a filesystem hierarchy.)
    38 There are now two separate kinds of {{{import}}}.
    40  * {{{import namespace "foo-1.3" Data.Foo}}}
    41  * {{{import Bar}}}
    43 The new semi-reserved word {{{namespace}}} is introduced, having special
    44 meaning only directly after the {{{import}}} keyword.  There is a
    45 ''level'' difference in what this new form of import means.  The
    46 declaration {{{import namespace}}} brings into availability the subset of
    47 the hierarchy of ''module'' names rooted in the package {{{"foo-1.3"}}},
    48 at the position {{{Data.Foo}}}.  That is, if the package {{{foo}}}
    49 version {{{1.3}}} contains the modules
    50  * Data.Foo.Bar
    51  * Data.Foo.Baz
    52  * Data.Bar
    53 then the namespace import brings into the "importable" namespace only
    54 the modules
    55  * Data.Foo.Bar
    56  * Data.Foo.Baz
    57 However, for the program to use those modules, it is still necessary to
    58 go ahead and actually {{{import}}} them in the normal way, although the
    59 names used to import them will now be ''relative'' to the available
    60 namespaces, rather than absolute.  So the declaration {{{import Bar}}}
    61 brings into scope all the entities defined in {{{Data.Foo.Bar}}}.  Like
    62 any normal import, these can be qualified or hidden.
    64 Thus,
    65  * {{{import namespace}}} brings into scope a bunch of names for modules
    66    from the given provenance.
    67  * {{{import}}} brings into scope a bunch of entities from the given
    68    module.
    70 === Naming a namespace ===
    72 Are namespaces first class?  Can we give them a name?  Indeed, why not?
    74  * {{{import namespace "foo-1.3" Data.Foo as OldFoo}}}
    75  * {{{import OldFoo.Bar}}}
    77 Here, we have declared that we want to be able to refer to the namespace
    78 as {{{OldFoo}}}, and so, a subsequent {{{import OldFoo.Bar}}}
    79 specifically asks for the {{{Data.Foo.Bar}}} from the package
    80 {{{foo-1.3}}}, just in case there might be a {{{Bar}}} module also
    81 available from another namespace.
    83 === What namespaces are available by default? ===
    85 If no namespaces are explicitly brought into scope, what modules are
    86 implicitly available?
    88  * Anything in the ''current'' package, i.e. the executable or library
    89    whose modules are all physically rooted at the same location in the
    90    filesystem as this module.
    92  * Is there an implicit {{{import namespace "base"}}}, just as there is an
    93    implicit {{{import Prelude}}}?
    95 === Namespace resolution ===
    97 In essence, namespaces take over the role formerly played by commandline
    98 arguments like {{{-Iproject}}} and {{{-package foo}}}.  The search path
    99 used by the compiler for finding modules is now partially declared in
    100 the source code itself.  (Note however that that the search path is
    101 declared symbolically, involving package names, not directories.  This is a very important
    102 separation of the thing itself from where it is stored.)
    104 Resolution of which module is referred to by an import statement (taking
    105 into account the namespaces) is just like the current process of
    106 resolving which entity is referred to by program text (taking into
    107 account the imported modules).  The source text may import multiple
    108 namespaces.  If any module import is ambiguous (i.e. the module exists
    109 in more than one namespace), it is a static error.  Resolution is lazy,
    110 in the sense that there is no error if namespaces contain the same
    111 module name, only if the program tries to import that module name.
    113 So when you say "import A.B.C", from what package does A.B.C come?
    115 There must be a single namespace in scope containing a module called
    116 {{{A.B.C}}}.  (Sidenote: or in fact a namespace called {{{A}}}, containing a module
    117 named {{{B.C}}})
    119 === Syntax ===
    121 The precise syntax can be debated.  New keywords like {{{use}}} or
    122 {{{from}}} could be substituted for {{{import namespace}}}.  The key
    123 important features however are the inclusion of:
    124  * the package name (mandatory)
    125  * an optional package version, if several are available
    126  * an optional path to use as the root of the available namespace
    127  * an optional renaming
    129 === Exports ===
    131 One might wonder whether it is now either necessary or desirable to
    132 permit ''namespaces'' to be re-exported in the same way that ''modules''
    133 can be?  For instance:
    135 {{{
    136 module Aggregate
    137   ( module Aggregate
    138   , namespace OldFoo
    139   ) where
    140 import namespace "foo-1.3" Data.Foo as OldFoo
    141 }}}
    143 The idea is that any module saying {{{import Aggregate}}} would thereby
    144 implicitly open the namespace of package {{{"foo-1.3"}}} at the root
    145 {{{Data.Foo}}}, in addition to having access to entities defined in
    146 {{{Aggregate}}} itself.
    148 Note that, just as with a current module re-export it is no longer
    149 possible for the importing location to use the original module name as a
    150 qualifier; so with a namespace re-export, there is no way to refer to
    151 the namespace in the importing location either.  It is purely a signal
    152 to the compiler telling it where to look for modules when resolving
    153 imports.
    155 I argue that namespace export ''is'' desirable, because it allows (but
    156 does not require) all package (namespace) dependencies to be gathered
    157 together in a single module for an entire project.  With such an
    158 organising principle, when dependencies change, there is only one source
    159 file to update.  But without namespace re-exports, it would be
    160 impossible to localise those dependencies to a single file.
    162 Note how this feature addresses several of the initial stated desires,
    163 of reducing the verbosity of imports, and of referring to leaf modules
    164 conveniently.  For instance:
    166 {{{
    167 module Gtk (namespace AllOfGtk) where
    168 import namespace "gtk-2.4" Graphics.UI.Gtk as AllOfGtk
    170 module MyGUI where
    171 import Gtk
    172 import Button
    173 ..... Button.label .....
    174 }}}
    176 === Implicit imports ===
    178 One could go further.  If I write a qualified name {{{M.e}}} in the
    179 source text, must I also write {{{import M}}} at the top?  The qualified
    180 entity is unambiguous, whether or not there is an explicit import for
    181 it, because the module qualification {{{M}}} must be unambiguous within
    182 the current namespaces.  In the Gtk example above, this would eliminate
    183 the need for {{{import Button}}}, and who knows how many other imports,
    184 leaving a single {{{import Gtk}}} to bring all of the qualified entities
    185 into scope.
    187 === Exposed vs Hidden packages ===
    189 GHC's scheme of exposed vs hidden packages can now be replaced with full
    190 source-code control of namespace visibility.  To setup a default set of
    191 exposed packages, you just write a module to export their namespaces:
    193 {{{
    194 module ExposedPackages
    195   ( namespace FGL
    196   , namespace Parsec
    197   , namespace HaXml
    198   ) where
    200 import namespace "fgl" as FGL
    201 import namespace "parsec-0.1" as Parsec
    202 import namespace "HaXml" as HaXml
    203 }}}
    205 and import it in every module of your project.  Or if importing it
    206 everywhere sounds too painful, one can even imagine that a compiler
    207 might provide a command-line option (or use a configuration file) to
    208 specify one distinguished module to be implicitly imported everywhere:
    210 {{{
    211 $ ghc --make -implicit-prelude=ExposedPackages MyProject.hs
    213 $ cat .ghci
    214 set -implicit-prelude ExposedPackages
    215 }}}
    218 === What if you wanted to import A.B.C from P1 and A.B.C from P2 into the ''same'' module? ===
    220 {{{
    221 module C1 (module A.B.C) where
    222 import namespace "P1"
    223 import A.B.C
    225 module C2 (module A.B.C) where
    226 import namespace "P2"
    227 import A.B.C
    229 module User where
    230 import qualified C1
    231 import qualified C2
    232 }}}