DIP10005: Dependency-Carrying Declarations is now available for community feedback

Chris Wright via Digitalmars-d digitalmars-d at puremagic.com
Sat Dec 31 08:41:16 PST 2016


On Fri, 30 Dec 2016 20:56:54 -0500, Andrei Alexandrescu wrote:

> On 12/30/16 7:32 PM, Chris Wright wrote:
>> On Fri, 30 Dec 2016 22:42:39 +0000, Stefan Koch wrote:
>>
>>> On Friday, 30 December 2016 at 22:29:18 UTC, Chris Wright wrote:
>>>> * Performance improvements, primarily when a module imports another,
>>>> bulky module for optional functionality.
>>> That is solved by selective imports.
>>
>> Well, not today, but it could be. Same with static imports. Both
>> accomplish this goal better than DIP1005.
> 
> Is this fact or opinion? If the former, could you please point where
> DIP1005 is getting it wrong. Thanks.

Consider:

  with(import std.range.primitives, std.traits)
  {
    template Foo(Range) if (isForwardRange!Range && !isArray!Range) {}
    template Bar(Range) if (isArray!Range) {}
  }

If I invoke Bar, the compiler must read and parse both 
std.range.primitives and std.traits.

With static imports:

  static import std.range.primitives, std.traits;
  template Foo(Range) if (std.range.primitives.isForwardRange!Range
      && !std.traits.isArray!Range) {}
  template Bar(Range) if (std.traits.isArray!Range) {}

If I invoke Bar, the compiler must read and parse std.traits but not 
std.range.primitives because it is unambiguous that isArray is in 
std.traits, not std.range.primivites.

With selective imports likewise.

You're going to respond that I'm only complaining that people can abuse 
the syntax. Consider that it also happens if I change the code and forget 
to update the imports. Also consider that people are likely to 
incorporate several related definitions that tend to use the same imports 
in order to save typing. "Be more virtuous" isn't a mantra that actually 
leads to better code.

>>>> * Making it easier to locate where things are defined when reading
>>>> code.
>>>
>>> That is solved by selective imports.
>>
>> Static imports do that better than selective imports, though it's more
>> typing.
> 
> So whether that's overall better is not settled, is it? We can't really
> define "better" as whatever each participant believes.

Whether the feature is overall better is not settled. Whether locating 
where symbols are defined is easier with DIP1005 than with static imports 
*is*.

Static imports tell you exactly where the symbol is defined at a glance, 
whereas with DIP1005, you have to look through at least two modules.

>> With selective imports, you can either find the declaration in the
>> current module, or find the symbol in an import list. If it's imported,
>> and the module author is using standard code formatting, you will find
>> the import at the top of the module, which will be fast. This is less
>> true with arbitrarily scoped imports.
>>
>> With DIP1005, you rely on there also being selective or static imports.
>> If the module author didn't use them, then you have to pull out grep.
> 
> DIP1005 allows the declaration to encapsulate its own dependencies. Of
> course if top-level imports are also present, the benefit erodes.

Consider:

  // tons of code
  with (import std.traits)
  template Bar(Range) if (isArray!Range && !isAbstractClass!Range) {}
  // tons more code

I can reasonably guess that the import is required for at least one of 
isArray and isAbstractClass, though for all I know, if I'm unfamiliar 
with std.traits, the import is unnecessary and both isArray and 
isAbstractClass are defined in the current module.

You can fix that with a static or selective import:

  with (import std.traits : isArray, isAbstractClass)
  template Bar(Range) if (isArray!Range && !isAbstractClass!Range) {}

Far less ambiguous, but your examples all have unmodified with-imports, 
and you seem less than keen about selective imports.

>> -- Another thing that just occurred to me: if you're modifying a
>> function signature and that brings in another imported symbol, you
>> don't have to move to the top of the file or struct to add the
>> necessary import. It's a small thing for vim users, who are used to
>> using marks and fast movement commands, but if you're using, say,
>> VSCode, that might be painful.
> 
> That's one of the many benefits of encapsulation.

It is a benefit of this DIP. There are many ways to achieve 
encapsulation, and there are many effects of encapsulation, and most of 
that is irrelevant to this discussion.

FWIW, you could also use current imports immediately before the relevant 
declaration. That would be a bad idea in general because it makes it much 
harder to locate imports when trying to locate a definition. 


More information about the Digitalmars-d mailing list