December 31, 2016
On Sat, 31 Dec 2016 07:23:14 -0500, Andrei Alexandrescu wrote:

> On 12/30/16 11:10 PM, Chris Wright wrote:
>> On Fri, 30 Dec 2016 21:13:19 -0500, Andrei Alexandrescu wrote:
>>> DIP1005 can't be in the business of arguing that encapsulation is good by means of examples.
>>
>> Right. I said we should talk about the concrete benefits of the proposal instead of talking about encapsulation.
> 
> Why would the DIP hamstring itself by not discussing its most important advantage? -- Andrei

DIP1005 provides concrete advantages. We can productively discuss its concrete advantages.

"Encapsulation" is an abstraction over a number of features. Many features can increase potential encapsulation, and this is only one of them. Encapsulation can provide a wide range of benefits, and this feature only provides one or two of them.

It saves us no effort to say "encapsulation". We still have to peel back that label to see what's underneath and evaluate that. But "encapsulation" is an applause light, so we automatically think better of the proposal than if we were merely looking at its concrete benefits.
December 31, 2016
On Sat, 31 Dec 2016 15:05:00 +0000, Adam D. Ruppe wrote:
> static import r = std.range.primitives;

For that to pass code review, you'd need a readable name -- perhaps 'ranges' instead of 'r' -- which makes it more obvious, more searchable, and more typing.

> with (import std.stdio) void process(File input) ;
> 
> Let's rewrite that to be:
> 
> template process() {
>     import std.stdio;
>     void process(File input) {
> 
>     }
> }

But that's a lot of typing, so nobody's going to do it. Just like how we use normal imports rather than static or selective imports everywhere. It's even more verbose than DIP1005.

> BTW it is my opinion that the "one file, one module" rule is a mistake. Even with all these things, the declarations are still not *guaranteed* to carry their dependencies, since top-level imports still leak in. Separate modules don't have that problem, and if we could just define several modules in one file, we'd basically destroy this DIP in one swift stroke.

That's rather elegant, though I don't see how you'd import a module in a file that defines several modules. If they were anonymous and importing the file gave you access to all of them, that makes sense. Otherwise it's tricky to figure out which sub-file modules exist.
December 31, 2016
On Saturday, 31 December 2016 at 15:05:00 UTC, Adam D. Ruppe wrote:
> Consider the following:
>
> with (import std.stdio) void process(File input) ;
>
> Let's rewrite that to be:
>
> template process() {
>    import std.stdio;
>    void process(File input) {
>
>    }
> }

I absolutely hate making functions templated if they don't have to be. It breaks a whole bunch of things and is not at all transparent to the user. You can't take its address, pass it as a delegate/function pointer, use certain traits to introspect it (e.g. isSomeFunction, ReturnType, etc.), not to mention how templates really don't work well with inheritance. This all goes out the window when you turn a regular function into a template.
December 31, 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.
December 31, 2016
On Sat, 31 Dec 2016 16:41:16 +0000, Chris Wright wrote:
> Far less ambiguous, but your examples all have unmodified with-imports, and you seem less than keen about selective imports.

Correction: *tend to* have unmodified with-imports.
December 31, 2016
On Tue, 13 Dec 2016 17:33:24 -0500, Andrei Alexandrescu wrote:

> Destroy.
> 
> https://github.com/dlang/DIPs/pull/51/files
> 
> 
> Andrei

> Inside any function, with (Import ImportList) is a statement that
> introduces a scope.

So I can't write:

  with (import std.stdio, std.conv)
  int count = readln().to!int;
  assert(count > 0);

It is in fact entirely equivalent to write:

  {
    import std.stdio, std.conv;
    int count = readln().to!int;
  }

I think I'd just put a line in the DIP: for consistency, this syntax works anywhere a with statement is currently allowed, but it's not recommended to use it inside function bodies in general.

> This extension removes an unforced limitation of the current with syntax (allows it to occur at top level)

In other words, another aspect of this DIP is that I can write:

  module foo;
  static import std.traits;
  static import bar;
  with (std.traits)
  {
    template Foo(T) if (isAbstractClass!T) {}
  }
  with (bar.SomeEnum)
  {
    enum something = SomeEnumValue;
  }

Which *almost*, but not quite, obviates the "you can put the import list in the with clause" part of the DIP. It's got all the same benefits for reading, but you might occasionally have to jump to the top of the file to add a new static import.
December 31, 2016
On Saturday, 24 December 2016 at 10:54:08 UTC, Stefan Koch wrote:
> If that were made more lazy, we could import half of the world with noticing impact.
>
> (Which espcially in std.traits, would not make that much of a difference since every template in there depends on nearly every other template in there)

Also the established technique of serializing precompiled AST (after semantic3) of modules to a cache should be applicable as well.

Cross-posting from https://github.com/dlang/DIPs/pull/51#issuecomment-269107966, b/c it wasn't answered yet.

Were any other means considered? This is proposing to add plenty of additional annotations only to speed up compilation, but none of the classical tools for pre-compilation were assessed.
Since D's modules don't have the header problem, even pre-compilation and reuse of semantic3 should be possible, or not?
January 02, 2017
On Saturday, 31 December 2016 at 17:02:55 UTC, Chris Wright wrote:
>> This extension removes an unforced limitation of the current with syntax (allows it to occur at top level)
>
> In other words, another aspect of this DIP is that I can write:
>
>   module foo;
>   static import std.traits;
>   static import bar;
>   with (std.traits)
>   {
>     template Foo(T) if (isAbstractClass!T) {}
>   }
>   with (bar.SomeEnum)
>   {
>     enum something = SomeEnumValue;
>   }

This is the only expression of the feature I've seen so far that makes intuitive sense to me.

I'm still not sold on it being a worthy addition but, if it were, then this is the most promising syntax I've seen so far.
January 02, 2017
On 02.01.2017 01:47, pineapple wrote:
> On Saturday, 31 December 2016 at 17:02:55 UTC, Chris Wright wrote:
>>> This extension removes an unforced limitation of the current with
>>> syntax (allows it to occur at top level)
>>
>> In other words, another aspect of this DIP is that I can write:
>>
>>   module foo;
>>   static import std.traits;
>>   static import bar;
>>   with (std.traits)
>>   {
>>     template Foo(T) if (isAbstractClass!T) {}
>>   }
>>   with (bar.SomeEnum)
>>   {
>>     enum something = SomeEnumValue;
>>   }
>
> This is the only expression of the feature I've seen so far that makes
> intuitive sense to me.
>
> I'm still not sold on it being a worthy addition but, if it were, then
> this is the most promising syntax I've seen so far.

Should be 'static with'. ('with' already has a meaning.)
January 02, 2017
On Mon, 02 Jan 2017 00:47:41 +0000, pineapple wrote:

> On Saturday, 31 December 2016 at 17:02:55 UTC, Chris Wright wrote:
>>> This extension removes an unforced limitation of the current with syntax (allows it to occur at top level)
>>
>> In other words, another aspect of this DIP is that I can write:
>>
>>   module foo;
>>   static import std.traits;
>>   static import bar;
>>   with (std.traits)
>>   {
>>     template Foo(T) if (isAbstractClass!T) {}
>>   }
>>   with (bar.SomeEnum)
>>   {
>>     enum something = SomeEnumValue;
>>   }
> 
> This is the only expression of the feature I've seen so far that makes intuitive sense to me.
> 
> I'm still not sold on it being a worthy addition but, if it were, then this is the most promising syntax I've seen so far.

This is a minor part of the proposal that's mentioned as an aside, though, and I'm not even sure it was even intentional.

This alone doesn't get the two benefits Andrei is most after -- specifically, that you can add the import in place without going to the top of the file and that you can move the declaration to another module without worrying about imports.