
Simon Peyton-Jones wrote:
Johan, if you are serious, do add a new wiki page to describe the design. You say it's simple, but I don't think it really is.
I'll support Johan by presenting the proposal for A below. I believe that it really is very simple, both in concept and implementation, but correct me if I am wrong. Of course, do not blame any of my errors on Johan, and do not assume that he supports this unless he says so. I'll support the consensus (so far) that B is the nicest. But if we can't get to a design for B that will actually be implemented within a reasonably short period of time, then please, let's try not to get stuck again. If this or some other design for A is indeed deemed to be simple, let's just do that if we can't do B. Perhaps we should set some time limit for B. I propose the following time limit: if a design for A has been tentatively approved and SPJ does not contribute to the discussion about B for one full month, then A automatically gets final approval and will be implemented immediately. [This has the additional advantage of giving SPJ motivation to remain engaged, because he seems to prefer B. :)] Proposal for A: Allow nested modules. A nested module can occur in any position that a data declaration can occur. The syntax of a nested module is the same as the syntax of a conventional module. A nested module must have the same hierarchical name as the module in which it is nested, followed by a single additional component. A name with a single component can be specified in the declaration of a nested module; this is syntactic sugar for the fully qualified name of the enclosing module followed by that single additional component. When a module M.A is directly nested in module M, there is an implied import in the enclosing module M as follows: import qualified M.A as A and an implied import in the nested module M.A as follows: import M These implied imports may optionally be specified explicitly with no effect, or overridden with other explicit imports, similarly to the usual implied import of Prelude. When modules are nested to a depth greater than one, similar implied imports exist for all recursively enclosing and enclosed modules, with the same rules about overriding. If an enclosing module M has an export list, a nested module N at any depth recursively cannot be imported by modules not nested inside M unless N is included in the export list of M. If M does not have an export list, N can be imported by any other module as usual. In every other respect, a nested module declaration has exactly the same effect as any other module declaration. In particular, the behavior of nested modules in the presence of all corner cases such as data families, etc., is specified by this rule. The effect of a nested module on the behavior of ghc --make is left unspecified as of now, until there is feedback from the GHC team. This would probably involve GHC looking for A.B and then A in turn when it fails to find A.B.C. Or perhaps even when A.B.C is found, to identify erroneous duplication. Or GHC could stay pretty much as it is now, relying on the user to ensure that GHC finds the nested module; that would certainly be fine for an initial implementation. Usage example: module Library where import Data.Text (Text) ... type ISBN = Text module Book where import Data.Text (Text) data T = New { name :: Text, iSBN :: ISBN } module Checkout where import Data.Time import qualified Library.Book as Book import qualified Library.Borrower as Borrower data T = New { book :: Book.T, borrower :: Borrower.T, dueDate :: Day } module Borrower where import Data.Text (Text) data T = New { name :: Text, address :: Text } module History where import qualified Library.Borrower as Borrower import qualified Library.Checkout as Checkout data T = New { borrower :: Borrower.T, checkouts :: [Checkout.T] } This makes available in the module Library the record types: Book.T, Checkout.T, Borrower.T, History.T with constructors: Book.New, Checkout.New, Borrower.New, History.New and record accessors: Book.name, Book.iSBN, Checkout.book, Checkout.borrower, Checkout.dueDate, Borrower.name, Borrower.address, History.borrower, History.checkouts I believe this specification should be very simple to implement and describe. There are some obvious shortcomings. But it does provide basic namespacing of records with almost no change to Haskell and GHC. Note also that you need to be careful to avoid mutually recursive imports. That is really more of a limitation of GHC than a limitation of the specification itself. Other compilers might not require that. I'd be happy to hear ideas about how to develop this simple idea further and eliminate some of the shortcomings, on condition that it doesn't lead to further bikeshedding and significant delay. One obvious enhancement would be for the implied import of the enclosing module to include also all names imported into the enclosing module, unlike the usual convention for imports. I'm not sure if there are complications to that though. Thanks, Yitz