February 07, 2017
On Tuesday, 7 February 2017 at 15:00:17 UTC, Andrea Fontana wrote:
> I don't understand why we can't use a template like:
>
> auto fun_time(SysTime)(SysTime tm)
> {
>     import std.datetime;
>     static assert (is(SysTime == std.datetime.SysTime));
>     return tm;
> }
>
> void main()
> {
>   import std.stdio;
>   import std.datetime;
>
>   fun_time(Clock.currTime()).writeln;
> }
>
> I think I missed something.

Because Systime's are used in the signiture, a local import cannot be used. Local imports are only evaluated if the function is used in the program and are local to that scope. Therefore, when the signitures are being evaluated, the compiler has no information about std.datetime, and therefore fails with a symbol not found error.
February 07, 2017
On Tuesday, 7 February 2017 at 15:00:17 UTC, Andrea Fontana wrote:
> ...

I think I misunderstood your comment. Please forgive the noise.

I need more coffee


February 07, 2017
On Tue, 07 Feb 2017 15:00:17 +0000, Andrea Fontana wrote:
> I don't understand why we can't use a template like:

You can. However, that makes things awkward when you want to pass that as a delegate. It makes it awkward to read. You don't get to specify the return type unless it happens to be the same as a parameter type.

A near equivalent is:

  template extractDate()
  {
    import std.datetime;
    Date extractDate(SysTime time)
    {
      return time.date;
    }
  }

Better type checking, but you need to refer to it as `extractDate!()`
sometimes. That's still better than having to refer to it as `extractDate!
(SysTime)`.
February 08, 2017
On Saturday, 4 February 2017 at 23:54:12 UTC, David Gileadi wrote:
>> That's obviously a self important lookup.
>
> This. So much this.

I'm afraid you are the only one who appreciate my humor :)
February 07, 2017
On 2/7/2017 7:00 AM, Andrea Fontana wrote:
> I don't understand why we can't use a template like:
>
> auto fun_time(SysTime)(SysTime tm)
> {
>     import std.datetime;
>     static assert (is(SysTime == std.datetime.SysTime));
>     return tm;
> }
>
> void main()
> {
>   import std.stdio;
>   import std.datetime;
>
>   fun_time(Clock.currTime()).writeln;
> }

It's an interesting idea, but it will have problems with overloading, as the template selection will match on everything.

February 08, 2017
On Friday, February 03, 2017 14:43:01 Dominikus Dittes Scherkl via Digitalmars-d wrote:
> Any thoughts?

This is really cool, but I have a couple of concerns about this and how it seems deficient in comparison to DIP 1005. I mentioned it in Andrei's PR for this, but no one has responded to my comment.

This first problem is UFCS. While this technique works great for parameters or on types you want to list in the template constraint, I don't see how it can work with UFCS. So, if you have something like

auto func(alias pred, R)(R range)
    if(isInputRange!R &&
       is(typeof(pred(range.front)) == bool))
{...}

you can do

auto func(alias pred, R)(R range)
    if(from!"std.range.primitives".isInputRange!R &&
       is(typeof(pred(range.front)) == bool))
{...}

but you can't import std.range.primitives.front to make dynamic arrays work, whereas with DIP 1005, you can just add the import to the function declaration, and it will work without having to tie the import to a specific symbol.

And while in many cases, you can just forgo UFCS - e.g.

auto func(P)(P param)
    if(is(typeof(from!"std.algorithm".find(param)) == param))
{...}

that not only doesn't work with the range primitives, but it's bad practice to use UFCS in the function body and not the template constraint (since the semantics may not be the same), meaning that not using UFCS in the template constraint means not using it in the function body for anything that's in the template constraint (which many folks won't like and others won't understand, resulting in subtle bugs), and any case where you want a function to work with types that define a function as a member function as well as with types that use a free function, you need to use UFCS and thus cannot choose to not use it in the template constraint.

So, unless there's something that I don't understand about this technique (which is definitely possible), it seems like it does not work with UFCS and thus makes it considerably worse than 1005 for a lot of templated code, much as it would work great for code that doesn't need UFCS.

The other problem is how much more verbose it is. With DIP 1005, you can do

with(import std.datetime)
auto foo(SysTime st1, SysTime st2, Duration d);

The import is only listed once, whereas with this technique, you have to list it for each symbol. e.g.

auto foo(from!"std.datetime".SysTime st1,
         from!"std.datetime".SysTime st2,
         from!"std.datetime".Duration d);

The result is much more verbose, and if you have several symbols that need imports between the return type, parameters, and template constraint, you quickly end up with a lot of extra text in the middle of your function signatures just because you want to tie the imports to the functions that use them. With DIP 1005, the imports are next to the function but separate where they avoid the need for repeating imports and don't get mixed into the middle of the function signature.

So, while the proposed technique is really cool and clever in what it lets us do without actually altering the language, it seems like it's quite a bit worse than DIP 1005. As such, I'm inclined to argue that we should favor DIP 1005 over this proposal, as cool as it is.

- Jonathan M Davis

February 09, 2017
On Thursday, 9 February 2017 at 05:40:01 UTC, Jonathan M Davis wrote:
> The import is only listed once, whereas with this technique, you have to list it for each symbol. e.g.
>
> auto foo(from!"std.datetime".SysTime st1,
>          from!"std.datetime".SysTime st2,
>          from!"std.datetime".Duration d);
>
> The result is much more verbose, and if you have several symbols that need imports between the return type, parameters, and template constraint, you quickly end up with a lot of extra text in the middle of your function signatures just because you want to tie the imports to the functions that use them.
> - Jonathan M Davis

Thank you for your insightful feedback, I'm currently pressed for time but I can at least begin to address one of your concerns.

Every proposed solution has some sort of tradeoff, verbosity could be dealt with for the price of turning a simple function into a template. In some cases this could be acceptable in others prohibitive.

auto
foo(alias dt = from!"std.datetime")
   (dt.SysTime  st1,
    dt.SysTime  st2,
    dt.Duration d)

Our "Implicit Function-Template Instantiation" also unfortunately bails with default arguments... maybe there is an enhancement request for that already?

@(from!"std.datetime") almost works too(it would avoid turning the function into a template), but has other issues.


February 10, 2017
On Thursday, 9 February 2017 at 05:40:01 UTC, Jonathan M Davis wrote:
> On Friday, February 03, 2017 14:43:01 Dominikus Dittes Scherkl via Digitalmars-d wrote:
>> Any thoughts?
>
> This is really cool, but I have a couple of concerns about this and how it seems deficient in comparison to DIP 1005.
Of course. This is why I called it a "workaround".
But in fact I would prefer to only use local imports instead of some impoting template. But with this workaround in mind, the
implementation of DIP1005 would be much easier (see below).

> [...]
> The other problem is how much more verbose it is. With DIP 1005, you can do
>
> with(import std.datetime)
> auto foo(SysTime st1, SysTime st2, Duration d);
>
> The import is only listed once, whereas with this technique, you have to list it for each symbol. e.g.
>
> auto foo(from!"std.datetime".SysTime st1,
>          from!"std.datetime".SysTime st2,
>          from!"std.datetime".Duration d);
>

With my original proposal you would write

auto foo(foo.M.SysTime st1,
         foo.M.SysTime st2,
         foo.M.Duration d)
{
   import std.datetime;

}



February 10, 2017
On Friday, 10 February 2017 at 13:28:43 UTC, Dominikus Dittes Scherkl wrote:
Sorry, accidentally hit some key-combination that immediately send the message that was not yet complete.

> With my original proposal you would write
>
> auto foo(foo.SysTime st1,
>          foo.SysTime st2,
>          foo.Duration d)
> {
>    import std.datetime;
     alias SysTime = std.datetime.SysTime;
     alias Duration = std.datetime.Duration;

> }

And the implementation of DIP1005 would only "create this alias automatically", so that you would write what you always did - if any type in a declaration is not found, the compiler tries to find it in the function definition and use that instead.

Therefore I would call this "auto aliasing"

It does not inquire a (visible) language change, only an enhanced lookup within the compiler.


February 10, 2017
On Thursday, 9 February 2017 at 05:40:01 UTC, Jonathan M Davis wrote:
> auto func(alias pred, R)(R range)
>     if(from!"std.range.primitives".isInputRange!R &&
>        is(typeof(pred(range.front)) == bool))
> {...}
>
> but you can't import std.range.primitives.front to make dynamic arrays work, whereas with DIP 1005, you can just add the import to the function declaration, and it will work without having to tie the import to a specific symbol.

That's a problem with UFCS in general. Workaround:

import std.functional;
import std.range;

void main(){
	auto r = [1];
	auto i = r.unaryFun!(std.range.front);
	assert(i == 1);
}

As an enhancement, it seems supporting value.(callable) would work:

range.(std.range.front)

That would be generally useful:

// print reciprocal of reduced range
range.reduce!someFunc.(n => 1 / n).writeln;

> The other problem is how much more verbose it is. With DIP 1005, you can do
>
> with(import std.datetime)
> auto foo(SysTime st1, SysTime st2, Duration d);
>
> The import is only listed once, whereas with this technique, you have to list it for each symbol.

That's a problem only when very few of a module's declarations need more than one symbol from the same module. How often does that occur when a module-level import should be avoided?