December 20, 2013
On 12/20/13, Martin Nowak <code@dawg.eu> wrote:
> On 12/19/2013 04:13 PM, Craig Dillabaugh wrote:
>> Maybe it uses the Javascript hypenator or something.  After all it was Andrei that posted it :o)
>
> Hyphenate with D. http://code.dlang.org/packages/hyphenate

P.S. That homepage link leads to a dead link. The repo link works though.
December 20, 2013
On Wednesday, 18 December 2013 at 21:40:08 UTC, Andrei Alexandrescu wrote:
> 2. Push imports from top level into the entities (functions, classes etc) that use them.

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.

Also, as was mentioned before, some imports (or lack of) will go unnoticed until the particular template or template path that it resides in gets instantiated. I don't relish the idea of thinking that my code is all clear, then getting some missing dependency error down the line after refactoring. This will make code more brittle.

December 20, 2013
On 12/20/13, Meta <jared771@gmail.com> 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.

Why is that important though? You only need to worry about these scoped imports if you actually use the code that has the scoped imports, rather than preemptively worry about imports at the top.

> I don't relish the idea of thinking
> that my code is all clear, then getting some missing dependency
> error down the line after refactoring. This will make code more
> brittle.

If you don't unittest your templates (which would catch these types of errors), then you have much more to worry about.
December 20, 2013
On 12/20/13 1:06 AM, Meta wrote:
> On Wednesday, 18 December 2013 at 21:40:08 UTC, Andrei Alexandrescu wrote:
>> 2. Push imports from top level into the entities (functions, classes
>> etc) that use them.
>
> 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.

I think that's a price we need to pay the same way cars replaced carriages in spite of them having a lot of disadvantages at the time. It's simply a mindset to do away with.

Transitive imports can be figured with:

dmd -c -o- -v module | grep '^import '

I felt the need for non-transitive imports, and I'm sure a tool will show up before long.

Local imports are the future. Better get used to them.

> Also, as was mentioned before, some imports (or lack of) will go
> unnoticed until the particular template or template path that it resides
> in gets instantiated. I don't relish the idea of thinking that my code
> is all clear, then getting some missing dependency error down the line
> after refactoring. This will make code more brittle.

Unittests/coverage to the rescue. Tooling tooling tooling.


Andrei

December 20, 2013
On 12/20/13, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> Local imports are the future. Better get used to them.

To be fair, it's modularization that is the future. And that implies modules that don't stand tall at 8K, 16K, 32K lines+. If the modules have less code and the code is limited in scope then you automatically need less imports.
December 20, 2013
On 2013-12-20 10:37, Andrei Alexandrescu wrote:

> I think that's a price we need to pay the same way cars replaced
> carriages in spite of them having a lot of disadvantages at the time.
> It's simply a mindset to do away with.
>
> Transitive imports can be figured with:
>
> dmd -c -o- -v module | grep '^import '
>
> I felt the need for non-transitive imports, and I'm sure a tool will
> show up before long.

I agree. It would be nice if an IDE could show a list of all imports, including locals, of a module.

-- 
/Jacob Carlborg
December 20, 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.
December 20, 2013
On Fri, Dec 20, 2013 at 10:06:01AM +0100, Meta wrote:
> On Wednesday, 18 December 2013 at 21:40:08 UTC, Andrei Alexandrescu wrote:
> >2. Push imports from top level into the entities (functions,
> >classes etc) that use them.
> 
> 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.

But we're only doing this for Phobos. You can still use global imports in your own modules. I think for Phobos this makes sense, because it's the standard *library*, which means all of its pieces will always be available (being, y'know, standard), and you really only care about what that particular piece of functionality you're using imports. When you import std.algorithm.find, you don't really care about what std.algorithm.cartesianProduct imports, do you?


> Also, as was mentioned before, some imports (or lack of) will go unnoticed until the particular template or template path that it resides in gets instantiated. I don't relish the idea of thinking that my code is all clear, then getting some missing dependency error down the line after refactoring. This will make code more brittle.

In your own code you can still use imports at the top of the file. We never said we're going to get rid of that.


T

-- 
IBM = I Blame Microsoft
December 20, 2013
On 12/20/13 5:45 AM, Joseph Rushton Wakeling wrote:
> On 20/12/13 10:06, Meta wrote:
>      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. ...
>          }
>      }

I had this idea fot a while, and Walter is favorable of it as well - extend "import" for one-shot use. With that feature the example would become:

    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
            && import.std.random.isUniformRNG!RandomGen)
    { ... }

In this case "import" would syntactically be placed at the beginning of a qualified name, meaning "import this module lazily and look up the symbol in it".

This would simplify quite a lot of two-liners into one-liners in other places, too.


Andrei

December 20, 2013
On Fri, Dec 20, 2013 at 09:27:55AM -0800, Andrei Alexandrescu wrote:
> On 12/20/13 5:45 AM, Joseph Rushton Wakeling wrote:
> >On 20/12/13 10:06, Meta wrote:
> >     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. ...
> >         }
> >     }
> 
> I had this idea fot a while, and Walter is favorable of it as well - extend "import" for one-shot use. With that feature the example would become:
> 
>     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
>             && import.std.random.isUniformRNG!RandomGen)
>     { ... }
> 
> In this case "import" would syntactically be placed at the beginning of a qualified name, meaning "import this module lazily and look up the symbol in it".
[...]

Hmm. Why do we need to incorporate the 'import' keyword in the first place? What about extending symbol lookup, so that if a fully-qualified symbol x.y.z can't be found in the current symbol tables, and x/y exists in the current import path, then implicitly try to import x.y and lookup z in that module. Then you could just write:

	void f(T)(T t) if (std.range.isInputRange!T) ...

and the compiler will automatically import std.range within that scope. Obviously, it's a bad idea to import std.range into module scope, since it would pollute the module namespace, but it seems a good idea to do a one-shot import automatically, since the qualified symbol itself already says which module the symbol is supposed to be defined in. There's no need to add another "import." prefix to it, IMO.


T

-- 
Today's society is one of specialization: as you grow, you learn more and more about less and less. Eventually, you know everything about nothing.