On Fri, Nov 03, 2017 at 04:30:21PM -0600, Jonathan M Davis via Digitalmars-d-learn wrote: > On Friday, November 03, 2017 14:52:22 H. S. Teoh via Digitalmars-d-learn > wrote: [...] > > Arguably, many of these large flat files ought to be split up into > > smaller files. For example, std.algorithm.* and std.range.* all > > tend to be conglomerations of basically-independent groups of > > structs and functions. Iota and Cycle never interact with each > > other, and neither of them interacts with Recurrence, yet these all > > sit in the same file. > > > > Seems to me like a typical case of low cohesion, which, according to > > the ideals of encapsulation, ought to be grounds for splitting up > > the file into self-contained parts. > > > > But of course, there are pragmatic reasons for the way things > > currently are. > > If you take that to the extreme, you just end up with a function per > module, which is pretty ridiculous IMHO (though that may be what you > mean by pragmatic reasons).
It's not that ridiculous if you consider that some of the more heavily-used Phobos functions are actually not individual functions, but overload sets. I know we have been trying to merge some of the unnecessary overload sets for other reasons, but some of these overload sets can get pretty big. Compound that with the long string of unittests associated with each overload, that has accumulated from a long string of bug fixes, etc., and you have pretty good justification for keeping just that single overload set in its own file, together with its associated unittests, and potentially also other paraphrenalia like enums or other declarations used by the overload set, but not used anywhere else. > And really, free functions like that are exactly the sort of places > where encapsulation tends not to matter much, because there's nothing > to encapsulate beyond the guts of the functions themselves, which are > already encapsulated. Arguably, once a function grows past a certain length, it's time to break it down into more manageable pieces. Having a function sit in its own file gives you a nice working space for this sort of refactoring, without worrying about namespace conflicts with unrelated code. Still, I agree that some degree of judgment is involved here. If we're talking about a simple function that will never grow past 5-10 lines, it does seem a little pointless to keep it in its own file, just to adhere to some ideal of encapsulation. But IME, with Phobos code even simple functions eventually grow into multi-page monsters over time, once you start adding type-specific specializations to deal with performance issues and the like. Just look at std.algorithm.searching.find, for example. Several different overloads, and static if blocks within an overload, sometimes on the lengthy side depending on the specific optimization being implemented. Arguably we should factor out some of the static if blocks or overloads into separate private functions for easier future maintenance / management if more specializations are added, or we need to balance between different optimizations based on different factors. Static-if / else blocks just aren't very conducive to this sort of thing. And being able to do this in a file dedicated to .find would be cleaner than having to do surgery in a 4600-line file (and having to deal with potential merge conflicts with other unrelated changes to the same file -- though git is so good at this that one generally doesn't run into much of a problem). > The situation is very different from something like std.container > where you have types which have member variables which could be > protected from access by other stuff in their modules if private > worked differently. Well, std.container *has* been split into submodules per container type. I think that's a much better organization than in std.algorithm.*. > Figuring out a good way to organize a bunch of free functions is > difficult (as evidenced by the fact that plenty of folks have a hard > time remembering what is where inside of std.algorithm and std.range), > but that's really not an encapsulation problem, just a code > organization problem. [...] <half-serious> Perhaps the solution is to go the one-overload-set-per-file route, with std/algorithm/package.d basically importing everything underneath. :-P </half-serious> (Shhh, don't tell Andrei, or we'll get another lecture about wasting time on worthless things while more important things are left to do.) T -- All problems are easy in retrospect.