December 20, 2013
On 12/20/13 9:38 AM, H. S. Teoh wrote:
> Hmm. Why do we need to incorporate the 'import' keyword in the first
> place?

To make the intent explicit. But that's an interesting idea. I'd probably require a leading '.' just to make it unambiguous that lookup must start from top level.

> 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) ...

Due to a bug that's actually the case today to some extent :o).


Andrei


December 20, 2013
On Fri, Dec 20, 2013 at 09:57:46AM -0800, Andrei Alexandrescu wrote:
> On 12/20/13 9:38 AM, H. S. Teoh wrote:
> >Hmm. Why do we need to incorporate the 'import' keyword in the first place?
> 
> To make the intent explicit. But that's an interesting idea. I'd probably require a leading '.' just to make it unambiguous that lookup must start from top level.
> 
> >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) ...
> 
> Due to a bug that's actually the case today to some extent :o).
[...]

"That's not a bug, that's an unintentional feature!" :-P


T

-- 
By understanding a machine-oriented language, the programmer will tend to use a much more efficient method; it is much closer to reality. -- D. Knuth
December 20, 2013
On 12/20/2013 9:57 AM, Andrei Alexandrescu wrote:
> On 12/20/13 9:38 AM, H. S. Teoh wrote:
>> Hmm. Why do we need to incorporate the 'import' keyword in the first
>> place?
>
> To make the intent explicit. But that's an interesting idea. I'd probably
> require a leading '.' just to make it unambiguous that lookup must start from
> top level.

Yes, it's interesting, but I think it's a bit too clever :-)

I think importing should be explicit, not implicit.

December 20, 2013
On Friday, 20 December 2013 at 17:57:46 UTC, Andrei Alexandrescu wrote:
> On 12/20/13 9:38 AM, H. S. Teoh wrote:
>> 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) ...
>
> Due to a bug that's actually the case today to some extent :o).
>
>
> Andrei

In regards to template restraints, it *would* be kind of nice to be able to trigger import only when overload resolution begins.

For example, just because a function requires "std.range.isInputRange" to validate its inputs/overloads, doesn't mean it *has* to be globally imported into the entire module.

In this sense, it would be nice to be able to define an "import" block for such a function:
//----
module wow;

void bar(T)(T t)
if (isInputRange!T)
import
{
    std.range;
}
body
{
    ....
}

void foo()
{
    ....
}

//----

With such an approach, "std.range" only gets imported if a call is "attempted" to bar. if no such call is even attempted, then range isn't imported at all.

In this example, "foo" simply does not know about range. It is scoped to bar.

As a client, *if* I make no calls to bar, then I will not trigger a dependency to range in wow at all.
December 20, 2013
On 2013-12-20 11:38, H. S. Teoh wrote:
> 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

one could make it a bit more explicit by requiring

	import std;

at module level, or even more explicit

	auto import std;

to enable lazy import of everything that is hierarchically below. could then be used for user libraries as well:

	import mybreadandbutter;		// has several sub folders / packages

/det

December 20, 2013
On 2013-12-20 17:27:55 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> 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.

How do you solve the problem that in D you can't tell the module name from a fully qualified names? For instance:

	import.std.something.somethingelse.anotherthing = 1;

Is the module std? std.something? std.something.somethingelse? It could be any of these answers.

	module std.something;
	struct somethingelse { static int anotherthing = 0; }

or

	module std.something.somethingelse;
	int anotherthing = 0;


-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca

December 20, 2013
On Fri, Dec 20, 2013 at 01:17:29PM -0600, captaindet wrote:
> On 2013-12-20 11:38, H. S. Teoh wrote:
[...]
> >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
> 
> one could make it a bit more explicit by requiring
> 
> 	import std;
> 
> at module level, or even more explicit
> 
> 	auto import std;
> 
> to enable lazy import of everything that is hierarchically below. could then be used for user libraries as well:
[...]

I like this idea. It could also be denoted as 'lazy import std', that is, if something references 'std.xyz.abc', then import the module std.xyz and lookup the symbol 'abc'. Then you could just use std.range.isInputRange in your signature constraints, and the import will only happen if you actually instantiate that template.

A more restricted variant, which may be more palatable to Walter, is to require explicit modules, i.e., you can't just say 'lazy import std', but you have to say 'lazy import std.range':

	lazy import std.range;

	void func(T)(T t)
		if (std.range.isInputRange!T)
	{
		...
	}

So the compiler at least knows that 'std.range' refers to some as-yet unimported module, then when you actually reference a symbol under std.range, the compiler will go and import the module to find its definition.


T

-- 
IBM = I'll Buy Microsoft!
December 20, 2013
On Fri, Dec 20, 2013 at 02:20:12PM -0500, Michel Fortin wrote:
> On 2013-12-20 17:27:55 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
> 
> >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.
> 
> How do you solve the problem that in D you can't tell the module name from a fully qualified names? For instance:
> 
> 	import.std.something.somethingelse.anotherthing = 1;
> 
> Is the module std? std.something? std.something.somethingelse? It could be any of these answers.
[...]

Using captaindet's idea of lazy imports, we could solve this problem:

	// Tell compiler that 'std.range' is a module, but don't import
	// it just yet.
	lazy import std.range;

	void func() {
		// Since the compiler already knows that 'std.range' is
		// a module, it knows to now import std.range and look
		// up 'InputRange.front' in it.
		auto x = std.range.InputRange.front;
	}


T

-- 
EMACS = Extremely Massive And Cumbersome System
December 20, 2013
On Friday, 20 December 2013 at 17:40:08 UTC, H. S. Teoh wrote:
> 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.

How about:

scope import std.range;
// lazy import std.range; ?

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

December 20, 2013
On Friday, 20 December 2013 at 19:34:10 UTC, Patrick Down wrote:
> On Friday, 20 December 2013 at 17:40:08 UTC, H. S. Teoh wrote:
>> 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.
>
> How about:
>
> scope import std.range;
> // lazy import std.range; ?
>
> void f(T)(T t) if (std.range.isInputRange!T) ...

I think the best keyword to use in this situation would be stati... Oh, dammit, not again.