phobos dependencies

Joseph Rushton Wakeling joseph.wakeling at webdrake.net
Fri Dec 20 05:45:07 PST 2013


On 20/12/13 10:06, Meta wrote:
> 2 comes with its own problems, though. With imports at the top, you can easily
> tell what the module imports at a glance. Putting imports into the deepest
> possible scopes where they are used will make it a huge chore to hunt all the
> imports down. You can of course grep for "import" (or ctrl+f for more plebeian
> editors), but you still don't have that singular list in one place that you can
> easily look at to tell what the module imports.

Conversely, one problem of imports at the top is that it's easy for unnecessary 
imports to hang around just because you don't have a direct association between 
the import and what it's actually used for.

I think the real issue we face is that it's not possible to really gain the 
advantages of deeply-nested-as-possible imports without _also_ breaking up the 
modules; we can't have Andrei's strategy (2) without some of strategy (3). 
Example: std.algorithm.topN, the only part of std.algorithm (apart from 
unittests) that requires std.random:

     void topN(alias less = "a < b",
             SwapStrategy ss = SwapStrategy.unstable,
             Range)(Range r, size_t nth)
         if (isRandomAccessRange!(Range) && hasLength!Range)
     {
         static assert(ss == SwapStrategy.unstable,
                 "Stable topN not yet implemented");
         while (r.length > nth)
         {
             auto pivot = uniform(0, r.length);
             // ... etc. ...
         }
     }

Now, at first glance it's easy to just insert an extra line before "auto pivot = 
...":

     import std.random : uniform;

... but it quickly becomes non-trivial if you want to do what really ought to be 
an option here, and allow a non-default RNG to be passed to the function:

     void topN(alias less = "a < b",
             SwapStrategy ss = SwapStrategy.unstable,
             Range, RandomGen)(Range r, size_t nth, ref RandomGen rng)
         if (isRandomAccessRange!(Range) && hasLength!Range
             && isUniformRNG!RandomGen)  // <--- needs std.random.isUniformRNG
     {
         static assert(ss == SwapStrategy.unstable,
                 "Stable topN not yet implemented");
         while (r.length > nth)
         {
             auto pivot = uniform(0, r.length, rng);
             // ... etc. ...
         }
     }

     // New function to support old 2-parameter version using default RNG
     void topN(alias less = "a < b",
             SwapStrategy ss = SwapStrategy.unstable,
             Range)(Range r, size_t nth)
         if (isRandomAccessRange!(Range) && hasLength!Range)
     {
         topN(r, ss, rndGen);    <---- needs std.random.rndGen;
     }

The second of the two needed imports is easy and can be nested inside the 
function, but the first -- the isUniformRNG template -- requires the import in 
the module's own scope.

So, to really allow std.algorithm to do away with its dependency on std.random, 
you need to break isUniformRNG and similar templates away from the rest of the 
std.random functionality.

(Yes, I know, you could break topN out from the rest of std.random, but remember 
that topN itself is only dependent on std.random for the case where you're using 
the unstable swap strategy.)

I'd hypothesize that probably it would be productive to start modularizing 
Phobos modules by separating out all the various helper templates like 
isUniformRNG, after which it should be much easier to avoid needing top-level 
module imports.


More information about the Digitalmars-d mailing list