
On Fri, 29 Sep 2006, Benjamin Franksen wrote:
Brian Hulley wrote:
Benjamin Franksen wrote:
At the danger of becoming completely off-topic now (sorry!), I have to say that I find /both/ versions ugly and unnecessarily hard to read. My personal solution is to generally avoid qualified imports.
How does this solution scale? Surely it's only lucky if people happen to choose names that don't clash with those of other modules?
Many, if not most, name clashes result from two different things actually being "the same concept". Such entities should be refactored into classes, rather than disambiguated using qualified names, if possible.
That's the problem. If one instance of such a conceptual method takes one additional parameter you cannot merge it in a class with another instance. What about, if the functions only differ in laziness? See for instance http://www.cs.york.ac.uk/fp/darcs/HaXml/src/Text/XML/HaXml/Parse.hs http://www.cs.york.ac.uk/fp/darcs/HaXml/src/Text/XML/HaXml/ParseLazy.hs
BTW, it has been argued by others that a single exported data type or class per module is a special case (however common) which cannot be generally assumed for library modules.
I like to use the style for the modules where it fits, and do differently where it does not fit. However I'm trying to make the modules fit, that is, I try to collect the functions concerning one data type in one module.
Even in the cmmon case where there is one 'main' data type exported, you often need auxiliary data types (which are often concrete types) to go along. I would find it strange to call main concept just 'T' while the minor, auxiliary stuff gets a 'real' name.
Not very nice, indeed. However, I would not call them 'real' names, but 'more special' names. Say Text.Html.T (main data type) Text.Html.Element (auxiliary type) Text.Html.Attribute (auxiliary type) Network.URI.T (main data type) Network.URI.Authority (auxiliary type)
As to scalability: In practice I always wait for the compiler to complain about ambiguous imports and only then fix like indicated above. A great feature of the Haskell module system is that it /never/ allows any ambiguity to actually appear in your source code. There is no way you could accidentally use an imported entity when it is not absolutely clear (to the compiler) which module it is imported from. OTOH, the compiler /only/ reports an error if you actually use such an ambigously imported name. Otherwise the above method of 'manual' disambiguation would indeed be very impractical.
When reading modules, I find it difficult to find out where identifiers come from. The foreign identifiers used in a module may not be defined in any of the directly imported modules. This is the case if an imported module only re-exports other module interfaces. Because of this I prefer imports like import A (f,g,h) import qualified B to import A import B Accidentally these are the only choices in Modula languages. :-)
If the type is not abstract, the advantage of calling it T is just that it avoids naming it twice (by type name and module name) in the situation where you want to not worry about name clashes with constructors of other types.
You never need to worry beforehand! You can rest assured that the compiler will mercilessly flag all ambiguous uses of imported names. Only if and ehen it does you need to start thinking about how you want to name these different entities in your module.
A program which is written as ambigous as the compiler allows makes reading such a program quite hard.
...and next come we name "the" function exported from each module 'f' or what? Imagine
set2 = Data.Set.Insert.f x set1
I have no preference for a particular name, but indeed, if a module merely implements one function and there are more modules (expected) with the same interface but different implementations, I would use the same identifier.