Changes between Version 4 and Version 5 of GhcPackagesWithGrafting


Ignore:
Timestamp:
Jul 7, 2006 9:43:12 AM (8 years ago)
Author:
malcolm
Comment:

moved to GhcPackageNamespaces

Legend:

Unmodified
Added
Removed
Modified
  • GhcPackagesWithGrafting

    v4 v5  
    1 [[PageOutline]] 
     1 
    22== Alternative Proposal for Packages (with grafting) == 
    33 
    4 GhcPackageNamespaces 
     4Moved to GhcPackageNamespaces to avoid the misleading title "Grafting" which is only incidental to the proposal. 
    55 
    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. 
    10  
    11 == A different, but related, problem == 
    12  
    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 [http://www.haskell.org/pipermail/libraries/2005-June/004009.html]) 
    17  
    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 [http://www.haskell.org/pipermail/libraries/2004-December/002800.html] 
    22  
    23 There are several desires in play here: 
    24  
    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 
    28  
    29 == Proposal == 
    30  
    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.) 
    37  
    38 There are now two separate kinds of {{{import}}}. 
    39  
    40  * {{{import namespace "foo-1.3" Data.Foo}}} 
    41  * {{{import Bar}}} 
    42  
    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. 
    63  
    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. 
    69  
    70 === Naming a namespace === 
    71  
    72 Are namespaces first class?  Can we give them a name?  Indeed, why not? 
    73  
    74  * {{{import namespace "foo-1.3" Data.Foo as OldFoo}}} 
    75  * {{{import OldFoo.Bar}}} 
    76  
    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. 
    82  
    83 === What namespaces are available by default? === 
    84  
    85 If no namespaces are explicitly brought into scope, what modules are 
    86 implicitly available? 
    87  
    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. 
    91  
    92  * Is there an implicit {{{import namespace "base"}}}, just as there is an 
    93    implicit {{{import Prelude}}}? 
    94  
    95 === Namespace resolution === 
    96  
    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.) 
    103  
    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. 
    112  
    113 So when you say "import A.B.C", from what package does A.B.C come? 
    114  
    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}}}) 
    118  
    119 === Syntax === 
    120  
    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 
    128  
    129 === Exports === 
    130  
    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: 
    134  
    135 {{{ 
    136 module Aggregate 
    137   ( module Aggregate 
    138   , namespace OldFoo 
    139   ) where 
    140 import namespace "foo-1.3" Data.Foo as OldFoo 
    141 }}} 
    142  
    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. 
    147  
    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. 
    154  
    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. 
    161  
    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: 
    165  
    166 {{{ 
    167 module Gtk (namespace AllOfGtk) where 
    168 import namespace "gtk-2.4" Graphics.UI.Gtk as AllOfGtk 
    169  
    170 module MyGUI where 
    171 import Gtk 
    172 import Button 
    173 ..... Button.label ..... 
    174 }}} 
    175  
    176 === Implicit imports === 
    177  
    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. 
    186  
    187 === Exposed vs Hidden packages === 
    188  
    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: 
    192  
    193 {{{ 
    194 module ExposedPackages 
    195   ( namespace FGL 
    196   , namespace Parsec 
    197   , namespace HaXml 
    198   ) where 
    199  
    200 import namespace "fgl" as FGL 
    201 import namespace "parsec-0.1" as Parsec 
    202 import namespace "HaXml" as HaXml 
    203 }}} 
    204  
    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: 
    209  
    210 {{{ 
    211 $ ghc --make -implicit-prelude=ExposedPackages MyProject.hs 
    212  
    213 $ cat .ghci 
    214 set -implicit-prelude ExposedPackages 
    215 }}} 
    216  
    217  
    218 === What if you wanted to import A.B.C from P1 and A.B.C from P2 into the ''same'' module? === 
    219  
    220 {{{ 
    221 module C1 (module A.B.C) where 
    222 import namespace "P1" 
    223 import A.B.C 
    224  
    225 module C2 (module A.B.C) where 
    226 import namespace "P2" 
    227 import A.B.C 
    228  
    229 module User where 
    230 import qualified C1 
    231 import qualified C2 
    232 }}} 
    233