
On 23 February 2006 11:14, Malcolm Wallace wrote:
"Simon Marlow"
wrote:
If modules are to contain interfaces, I don't think extending the export list is the way to do it. I'd rather do something like: -------------------------------------- modules M exports
class Eq a where (==) :: a -> a -> Bool
data T :: * -> * f :: T -> Int mkT :: Int -> T
where -- implementation below here --------------------------------------
But, apart from adding fuller signatures than I proposed, that is exactly the same as an export list, modulo some very minor syntactic differences!
Of course it's the same as the export list, but with different syntax, and extended to allow specifying the full interface. That's the point ;-) The main difference is that I'm doing away with parentheses, commas, and export specifiers, and using layout and full declaration syntax instead. (I don't really want to discuss this very rough idea any more though, it's just a distraction, and I'm not sure I like it anyway). Cheers, Simon

Hello Simon, Thursday, February 23, 2006, 2:41:11 PM, you wrote:
modules M exports
class Eq a where (==) :: a -> a -> Bool
data T :: * -> * f :: T -> Int mkT :: Int -> T
where -- implementation below here --------------------------------------
SM> The main difference is that I'm doing away with parentheses, commas, and SM> export specifiers, and using layout and full declaration syntax instead. SM> (I don't really want to discuss this very rough idea any more though, SM> it's just a distraction, and I'm not sure I like it anyway). i like this idea. text editor's extensions then should maintain this export list. you just hit special key at the type/class/function definition to (re)move it to export list opposite alternative is to add "export" modifiers to all types of declarations and use tools to extract interface part of .hs file anyway, we can't establish an "ultimate" solution without use of tools. all other variants, including current one, is just half-solutions -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

modules M exports
class Eq a where (==) :: a -> a -> Bool
data T :: * -> * f :: T -> Int mkT :: Int -> T
where -- implementation below here --------------------------------------
SM> The main difference is that I'm doing away with parentheses, commas, and SM> export specifiers, and using layout and full declaration syntax instead. SM> (I don't really want to discuss this very rough idea any more though, SM> it's just a distraction, and I'm not sure I like it anyway).
let's have a closer look before we dump this again, shall we?-) if you'd go one step further, you'd replace the public/private modifiers with public/private module sections. I don't like the modifiers, and I'm uncertain about the intermediate form you suggested, but I might be able to live with two-section modules: nothing is duplicated, the public and private items are clearly grouped. if you like to make the public section look more like an interface, you only use short definitions in it (could this be made to work for data type constructors/class members?). another neat consequence not available with the other alternatives: instances could be private (without having to be named), instead of flooding into importing modules. proper interfaces would still be nice (and useful!), but the general opinion seems to be that they're beyond Haskell' (what isn't, really?-). about tools: tools can relieve some of pressure on the language design (which is why I'm more in the camp "a language definition should not ignore tools" now!-). but the wrong language design can make the tools' job awkward, so it may be useful to look at what a tool can/cannot do: 1 ides may let you browse interfaces, but they don't give a standard form of printing those interfaces with the modules they belong to, nor even a standard form of those interfaces.. 2 ides let you see different locations in your sources in a single window (I use this whenever I need to modify the imports while editing in the middle of my module; I sometimes use this to see the definition of the data types I'm working on). that means that the link between definitions and import lists can be implicit, and browsing may bypass import lists/ export interfaces entirely. that holds for both browsing and navigation. 3 ides can add/remove items from the imports "remotely" (HaRe does this). they can also generate explicit export/import lists (even my old Hugs.vim supported that), and add types in comments, but if they do so, it is not clear what to do with comments already present (update any types in them? what if they were meant to document alternative/ old/comming versions..; leave any unidentified comments alone? then we'll duplicate type comments as soon as someone adds any text to the automatically generated non-interface..) 4 ides can use tooltips to tell you whether the stuff you're looking at is exported or not, but again, that won't usually make it into printouts. look at the problem from this perspective, and you see that - haskell98 fails 1/4, profits from (and really needs) 2, doesn't support 3 all that well - public/private modifiers only help with 4, part of their job is covered by 3 already - public/private sections solve 1/4, may still use 2/3 so, public/private sections seem to support all pragmatic issues, and since they're still just haskell code, don't suffer from duplication/scoping/.. issues, either. in fact, we might end up simplifying the syntax be removing export lists! but exposing only some data constructors of a data type would be awkward? cheers, claus

Hello Claus, Thursday, February 23, 2006, 9:48:03 PM, you wrote: CR> if you'd go one step further, you'd replace the public/private modifiers CR> with public/private module sections. I don't like the modifiers, and I'm CR> uncertain about the intermediate form you suggested, but I might be CR> able to live with two-section modules: CR> nothing is duplicated, the public and private items are clearly grouped. in my programs and other programs i seen (including GHC and standard libraries) sections cover the related functions and types. part of them are exported and part not exported -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

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 exports <body> where <body> -------------- 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. moreover: 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 section. 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). cheers, claus (*) actually, that would work independently of the rest of this message, with the current system, if applied to both export and import lists.

"Simon Marlow"
The main difference is that I'm doing away with parentheses, commas, and export specifiers, and using layout and full declaration syntax instead. (I don't really want to discuss this very rough idea any more though, it's just a distraction, and I'm not sure I like it anyway).
I like this general idea, I was thinking about something similar a long time ago. But in case of a large datatype, e.g. an AST, we certainly don't want to duplicate it in whole. It should be sufficient in the export section. So perhaps what is really needed is the ability to split a module into a public part and a private part, and allowing to duplicate certain definitions in a partial form, e.g. have both 'data Foo' and 'data Foo = Con1 | Con2' in the same module. The details are not obvious though because the syntax has not been designed with this in mind. -- __("< Marcin Kowalczyk \__/ qrczak@knm.org.pl ^^ http://qrnik.knm.org.pl/~qrczak/

On Thu, 2006-02-23 at 21:04 +0100, Marcin 'Qrczak' Kowalczyk wrote:
"Simon Marlow"
writes: The main difference is that I'm doing away with parentheses, commas, and export specifiers, and using layout and full declaration syntax instead. (I don't really want to discuss this very rough idea any more though, it's just a distraction, and I'm not sure I like it anyway).
I like this general idea, I was thinking about something similar a long time ago.
But in case of a large datatype, e.g. an AST, we certainly don't want to duplicate it in whole. It should be sufficient in the export section.
So perhaps what is really needed is the ability to split a module into a public part and a private part, and allowing to duplicate certain definitions in a partial form, e.g. have both 'data Foo' and 'data Foo = Con1 | Con2' in the same module. The details are not obvious though because the syntax has not been designed with this in mind.
It might be useful for people who are interested in this idea to look at Mercury's module system. It is very similar to the one proposed. Also, Mercury is quite close to Haskell (type classes and so on). Incidentally, I'm told that the design was influenced by Ada. Cheers, Bernie.
participants (5)
-
Bernard Pope
-
Bulat Ziganshin
-
Claus Reinke
-
Marcin 'Qrczak' Kowalczyk
-
Simon Marlow