December 15, 2016
On Thursday, 15 December 2016 at 16:16:51 UTC, Walter Bright wrote:
> On 12/14/2016 5:26 AM, Dominikus Dittes Scherkl wrote:
>> Why not leave it as it is and only change the compiler to
>> perform inputs _within_ a function before evaluating the declaration, so that
>> the symbols imported can be used in the declaration?
>>
>> e.g.
>>
>> fun(Range x) if(isInputRange!x)
>> {
>>    import std.range;
>>    auto a = x.front();
>> }
>
> That would make it problematic to have function declarations.

Yeah, but declarations ARE already problematic, because local imports are not visible from them, so only having a declaration file (.di) at the moment already is not enough to determine the dependencies.
And as I understand you, you don't plan to remove the local imports as they are now? (would be a really huge breaking change!)

So, I no longer propose to change nothing except the internal compiler behaviour.
Now I propose to additionally change the .di-file generation to also add all local imports to the start of the declaration file, so that having this file gives ALL dependencies of the declared stuff.
December 15, 2016
On Thursday, 15 December 2016 at 17:07:35 UTC, Dominikus Dittes Scherkl wrote:
> On Thursday, 15 December 2016 at 16:16:51 UTC, Walter Bright wrote:
>> On 12/14/2016 5:26 AM, Dominikus Dittes Scherkl wrote:
>>> Why not leave it as it is and only change the compiler to
>>> perform inputs _within_ a function before evaluating the declaration, so that
>>> the symbols imported can be used in the declaration?
>>>
>>> e.g.
>>>
>>> fun(Range x) if(isInputRange!x)
>>> {
>>>    import std.range;
>>>    auto a = x.front();
>>> }
>>
>> That would make it problematic to have function declarations.
> So, I no longer propose to change nothing except the internal compiler behaviour.
> Now I propose to additionally change the .di-file generation to also add all local imports to the start of the declaration file, so that having this file gives ALL dependencies of the declared stuff.

This makes me think about the current .di generation. As far as I remember it only strips function bodies and leaves everything else intact. It could already try to remove unused imports. And in the future it could replace regular imports with auto-generated DCD-style imports.

December 15, 2016
On 12/15/2016 01:06 PM, Andrej Mitrovic wrote:
> On Thursday, 15 December 2016 at 17:07:35 UTC, Dominikus Dittes Scherkl
> wrote:
>> On Thursday, 15 December 2016 at 16:16:51 UTC, Walter Bright wrote:
>>> On 12/14/2016 5:26 AM, Dominikus Dittes Scherkl wrote:
>>>> Why not leave it as it is and only change the compiler to
>>>> perform inputs _within_ a function before evaluating the
>>>> declaration, so that
>>>> the symbols imported can be used in the declaration?
>>>>
>>>> e.g.
>>>>
>>>> fun(Range x) if(isInputRange!x)
>>>> {
>>>>    import std.range;
>>>>    auto a = x.front();
>>>> }
>>>
>>> That would make it problematic to have function declarations.
>> So, I no longer propose to change nothing except the internal compiler
>> behaviour.
>> Now I propose to additionally change the .di-file generation to also
>> add all local imports to the start of the declaration file, so that
>> having this file gives ALL dependencies of the declared stuff.
>
> This makes me think about the current .di generation. As far as I
> remember it only strips function bodies and leaves everything else
> intact. It could already try to remove unused imports. And in the future
> it could replace regular imports with auto-generated DCD-style imports.
>

Please make this an issue. Thanks! -- Andrei
December 15, 2016
Some more details on my proposa based on UDA:

// applies to next decl
@deps!({import std.algorithm;})
void test1(){}

// applies to a set of decls
@deps!({import std.stdio;}){
  void test2(){}
  void test3(){}
}

// applies to all following decls (':')
@deps!({import std.array;}):

// can specify other dependencies beyond imports and have arbitrary complex
logic:
@deps!({
  import std.range;
  static int[100] data2;
  version(linux){
    enum data1=import("foo");//string import
    pragma(lib, "curl");
  }
}):
void test4(){}

// Can alias some dependencies:
alias deps1=deps!({import std.algorithm;});

@deps1
void test4(){}

NOTE: the above code compiles if we add
`struct deps(T...){}`, but that logic would be implemented in the compiler.

Advantages:

* no new syntax (just new semantics)
* no nesting inside {} required, so no additional indentation
* allows grouping multiple declarations under a single set of imports
* `grep import` will still work unlike many of the existing proposals
* can use existing traits to query for such imports (eg getSymbolsByUDA)
* allows a more general solution (eg also handles `pragma(lib, "curl");` if
we want that as well)

(Note: ignore the `variant 2` i had described above)

On Thu, Dec 15, 2016 at 8:32 AM, Andrei Alexandrescu via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 12/15/2016 11:11 AM, Walter Bright wrote:
>
>> On 12/15/2016 6:53 AM, Andrei Alexandrescu wrote:
>>
>>> The document does specify the advantages and disadvantages of lazy imports, as follows:
>>>
>>> ===
>>> * Full lazy `import`s. Assume all `import`s are lazy without any
>>> change in the
>>> language. That would allow an idiom by which libraries use fully
>>> qualified names
>>> everywhere, and the `import`s would be effected only when names are
>>> looked up.
>>> This would allow scalability but not the dependency-carrying aspect.
>>> ===
>>>
>>
>> That would be a massive breaking change.
>>
>
> This may be a misunderstanding. The idiom would be opt-in so existing behavior will be preserved (albeit it won't benefit of the improvements).
>
> Andrei
>
>


December 15, 2016
On 12/15/2016 02:22 PM, Timothee Cour via Digitalmars-d wrote:
> Some more details on my proposa based on UDA:
>
> // applies to next decl
> @deps!({import std.algorithm;})
> void test1(){}
>
> // applies to a set of decls
> @deps!({import std.stdio;}){
>   void test2(){}
>   void test3(){}
> }
>
> // applies to all following decls (':')
> @deps!({import std.array;}):
>
> // can specify other dependencies beyond imports and have arbitrary
> complex logic:
> @deps!({
>   import std.range;
>   static int[100] data2;
>   version(linux){
>     enum data1=import("foo");//string import
>     pragma(lib, "curl");
>   }
> }):
> void test4(){}
>
> // Can alias some dependencies:
> alias deps1=deps!({import std.algorithm;});
>
> @deps1
> void test4(){}
>
> NOTE: the above code compiles if we add
> `struct deps(T...){}`, but that logic would be implemented in the compiler.

I now understand the idea, thank you.

My question is, doesn't this take things too far? Earlier I wrote:

> The acceptability of the proposal decays exponentially with its
> deviation from existing import syntax.

Indeed adding less syntax is better, but that's not an absolute; the optimum isn't necessarily at the "zero syntax added" point. This is because there are several things to harmonize in language design, which naturally are in tension.


Andrei

December 15, 2016
On Thursday, 15 December 2016 at 16:11:56 UTC, Walter Bright wrote:
> That would be a massive breaking change.

SDC do parse the module only when an identifier resolution reach top level, and then populate the module's top level symbol table without running any semantic analysis on any of its symbols.

Symbol are analyzed on demand when they are used. I think this achieve something close enough to what Andrei was proposing and is not breaking.

You may also want to look at https://www.youtube.com/watch?v=b_T-eCToX1I to see what clang's up to.
December 15, 2016
On 12/15/16 4:48 PM, deadalnix wrote:
> On Thursday, 15 December 2016 at 16:11:56 UTC, Walter Bright wrote:
>> That would be a massive breaking change.
>
> SDC do parse the module only when an identifier resolution reach top
> level, and then populate the module's top level symbol table without
> running any semantic analysis on any of its symbols.
>
> Symbol are analyzed on demand when they are used. I think this achieve
> something close enough to what Andrei was proposing and is not breaking.

Yah, that would work (note we are not talking about breakage; perhaps you meant no language addition?). That would (a) miss the dependency carrying aspect and (b) would be suboptimal. The moment any unqualified symbol is looked up in a module, all modules it imports need to be loaded. This could be avoided by using consistently fully qualified symbols.

The comment about alternatives in the DIP does mention that but without going into details. Let me know if you think they should be added.


Andrei


December 15, 2016
On 12/13/16 11:33 PM, Andrei Alexandrescu wrote:
> Destroy.
>
> https://github.com/dlang/DIPs/pull/51/files
>
>
> Andrei

On first it seems like an awesome idea. That solves ... but wait what? Thinking more  about the problem at hand - I fail to see what this DIP accomplishes that can't be done better today.

1. The benefit of placing import to each function is based on the untold assumption that we have:
a) huge modules with many functions
b) that constraints of these functions require different (large) modules

The reality is far from this picture - if anything 99% of template constraints are dependent on std.range.primitives and std.traits with a bit of std.meta from time to time. So we'd have a boilerplate of

auto foo(R)(R range)
(import std.range.primitives)
if(isInputRange!R){ ... }

everywhere for no noticeable benefit - touch one of functions and you get full set of imports of these _small_ modules.

2. By itself the mechanism for delaying import even for constraint until the function is touched is moot as long as the module in question is huge and is not split in pieces. In other words:

auto foo(R)(R range)
(import std.range)
if(isInputRange!R){ ... }

Pulls in full std.range the moment foo is touched, compared to

import std.range.primitives;
...
auto foo(R)(R range)
if(isInputRange!R){ ... }

which is because it actually isolates the whole mess of complete std.range from the user of a template.

All in all my practical response is split the modules at least in 2 parts: constraints + full functionality, then import the one with constraints at the top level. Works today and solves the practical issues unlike the proposal.

---
Dmitry Olshansky
December 15, 2016
On Thursday, 15 December 2016 at 19:52:50 UTC, Andrei Alexandrescu wrote:
> On 12/15/2016 02:22 PM, Timothee Cour via Digitalmars-d wrote:
>> Some more details on my proposa based on UDA:
>>
>> ...
>>
> I now understand the idea, thank you.
>
> My question is, doesn't this take things too far? Earlier I wrote:
>
>> The acceptability of the proposal decays exponentially with its
>> deviation from existing import syntax.
>
> Indeed adding less syntax is better, but that's not an absolute; the optimum isn't necessarily at the "zero syntax added" point. This is because there are several things to harmonize in language design, which naturally are in tension.
>
>
> Andrei

Something like Timothee Cour's @deps proposal is interesting,
because it adds the same options to DCD's as module level imports have.

They can be applied to any symbol that supports udas which the current syntax from the dip doesnt.

As he displayed, they can be used to group symbols together which makes it more dry, but that can also be a drawback when refactoring depending which version you use.

And you dont have to support all shown features as that syntax is easier extendable.

The initial version could support only import statements.
December 15, 2016
one more thing:

we can simplify further (while still having formatted looking code) with
!q{} instead of !() :

```
// applies to next decl
@deps!q{import std.algorithm;}
void test1(){}

// applies to a set of decls
@deps!q{import std.stdio;}{
  void test2(){}
  void test3(){}
}

// applies to all following decls (':')
@deps!q{import std.array;}:

// can specify other dependencies beyond imports and have arbitrary complex
logic:
@deps!q{
  import std.range;
  static int[100] data2;
  version(linux){
    enum data1=import("foo");//string import
    pragma(lib, "curl");
  }
}:
void test4(){}

// Can alias some dependencies:
alias deps1=deps!q{import std.algorithm;};

@deps1
void test4(){}
```



On Thu, Dec 15, 2016 at 3:46 PM, ArturG via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Thursday, 15 December 2016 at 19:52:50 UTC, Andrei Alexandrescu wrote:
>
>> On 12/15/2016 02:22 PM, Timothee Cour via Digitalmars-d wrote:
>>
>>> Some more details on my proposa based on UDA:
>>>
>>> ...
>>>
>>> I now understand the idea, thank you.
>>
>> My question is, doesn't this take things too far? Earlier I wrote:
>>
>> The acceptability of the proposal decays exponentially with its
>>> deviation from existing import syntax.
>>>
>>
>> Indeed adding less syntax is better, but that's not an absolute; the optimum isn't necessarily at the "zero syntax added" point. This is because there are several things to harmonize in language design, which naturally are in tension.
>>
>>
>> Andrei
>>
>
> Something like Timothee Cour's @deps proposal is interesting,
> because it adds the same options to DCD's as module level imports have.
>
> They can be applied to any symbol that supports udas which the current syntax from the dip doesnt.
>
> As he displayed, they can be used to group symbols together which makes it more dry, but that can also be a drawback when refactoring depending which version you use.
>
> And you dont have to support all shown features as that syntax is easier extendable.
>
> The initial version could support only import statements.
>


3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19