On Wed, 28 Mar 2012 21:53:57 -0400, Jesse Phillips <[email protected]> wrote:

I won't be going out of my way to check this, but there is a mention of adding the range primatives. This works, but it doesn't make the class a range for any other module, so std.algorithms won't recogonise it as a range.

At first thought, I believed this should be fixable -- if not working already. Consider that std.algorithm doesn't include *your* module, yet you can pass types defined in your module into std.algorithm and it works just fine.

But I realized after typing about 2 messages in response to this (and deleting them), you are right, there is a fundamental problem here. Because the template instantiation is based solely on the type. It does *not* include the type and whatever other modules you may have included that could define extension methods. I don't think it's an implementation issue, I think it's a design issue -- there simply is no way to do this.

A counter case:

module1.d:

int foo(T)(T t)
{
   return t.bar();
}

module2.d:

struct S { int x;}

module3.d:

import module1, module2;

int bar(S s) { return s.x * 2;}

void baz1()
{
   S s(2);
   assert(foo(s) == 4);
}

module4.d:

import module1, module 2;

int bar(S s) { return s.x * 3;}

void baz2()
{
   S s(2);
   assert(foo(s) == 6);
}

// and to drive the point further:
module5.d:
import module3, module4;

void main()
{
   baz1();
   baz2();
}

In order for the asserts to *both* pass, there has to be two different instantiations of foo!S, one for module3, and one for module4.

So two possible sane rules:
1. A template instantiation can *only* use UFCS from functions defined in or imported from the module in which the template is defined. (i.e. the way it works now I think)
or
2. A template instantiation can *only* use UFCS from functions defined in or imported from the module in which the template is defined, *and* from functions as defined or imported by the module that defines the type on which UFCS is being used. In other words, from my example above, only functions defined in or imported from module1.d and module2.d. Therefore, the bar extension defined in module3 and module4 cannot be called from module1.

For builtin types (such as arrays or numbers), there wouldn't be a module that the type was defined. However, object.di is imported by everything, so extensions could be put in there.

This kind of puts a damper on certain expectations for how UFCS could be used. But I don't see any other way (other than adding the current module into the instantiation somehow -- imagine the template bloat...).

Even with this limitation, UFCS still allows a lot of cool things. One misleading suggestion from the article however, it's not very easy to create non-friend non-member functions using UFCS, considering that every function in a given module is a friend. In order to do this, you would need a helper module for each module that wants to define such non-friend functions. Given the above proof, the helper module would also have to be imported by the main module.

-Steve

Reply via email to