UFCS for D
Steven Schveighoffer
schveiguy at yahoo.com
Thu Mar 29 16:34:00 PDT 2012
On Wed, 28 Mar 2012 21:53:57 -0400, Jesse Phillips
<jessekphillips+D at gmail.com> 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
More information about the Digitalmars-d-announce
mailing list