where to find Implements!

Jonathan M Davis jmdavisProg at gmx.com
Sun Oct 3 15:47:07 PDT 2010


On Sunday 03 October 2010 14:32:29 BLS wrote:
> On 30/09/2010 00:40, Jonathan M Davis wrote:
> > hich has the appropriate methods will work for any function that
> > requires a particular API on the type or types that it's dealing with.
> > There's no need to have any interfaces. They just work if they have the
> > correct API and don't work if they don't. The isRandomAccessRange()
> > template and its friends are used in template constraints to improve the
> > error messages you get when a type doesn't have the correct API, but
> > they aren't strictly-speaking necessary.
> 
> Thanks you for taking the time to explain, Jonathan!
> It seems however, that these template constraints are just made for
> ranges. I maybe wrong but I can't see a general purpose value. I am also
> not able to see why an additional vtable entry is necessary to do
> ordinary  /implements/ validation work. (Finally it is just a contract,
> which Mr compiler should negotiate)
> 
> Bjoern

With an interface, you can declare a variable of that type. e.g.

interface I
{
	//...
}

class C : I
{
	//...
}

void main()
{
	I i = new C();
}


The compiler then has to deal with the fact that a variable of an interface 
could be of any class that implements that interface. That requires a vtable. 
The duck typing that templates give you don't allow for anything of the sort. 
You can't declare a variable of type RandomAccessRange.

A good example of templates and APIs would be a function that requires a 
particular type be compareable with the less-than operator:

void func(T)(T a, T b)
{
	//...
	if(a < b)
	//...
}


When func is instantiated with a given type, it will generate a copy of the 
template with that type. If the generated code is valid, it will compile. If 
it's not, it won't. So, func(5, 4) would create

void func!int(int a, int b)
{
	//...
	if(a < b)
	//...
}

which as far as we can see with the code given is legal and works. However, what 
if you have a struct S which doesn't implement opCmp() and therefore does not 
have a less-than operator?

void func!S(S a, S b)
{
	//..
	if(a < b)
	//...
}

The generated code won't compile because a < b is not valid. And the error 
message may or may not be particularly clear. To improve the error message, we'd 
add a template constraint

void func(T)(T a, T b)
	if(__traits(compiles, a < b))
{
	//...
	if(a < b)
	//...
}

Now the compiler won't even try and instantiate the template with a type that 
can't be compared with less-than. It will complain about the type not matching 
the template (because the template constraint isn't true). There is nothing here 
about implementing any sort of interface. There is nothing here about declaring 
variables of type CanBeComparedWithLessThan. There is no need for a vtable.

Now, that's a fairly simple example, but what if you have a more complex one, 
that depending on the type either used == or <.

void func(T)(T a, T b)
	if(__traits(compiles, a < b) ||
	   __traits(compiles, a == b))
{
	//...

	static if(__traits(compiles, a < b))
	{
		//...
		if(a < b)
		//...
	}

	//...

	static if(__traits(compiles, a == b))
	{
		//...
		if(a == b)
		//...
	}

	//...
}


The code that is generated and what the function actually does could change 
drastically depending on whether T has < or it has ==. You could even 
intermingle additional static ifs and/or static ifs that required both < and ==, 
so that the fact that T has both < and == could generate code that differed that 
much more. With that sort of power, you can do stuff way more complicated than 
require that a type have a particular function or set of functions. With an 
interface, the set of functions is fixed. With templates, it could vary. You 
could have two completely different set of APIs which both satisfy a particular 
template and there fore both have the "interface" that that template is looking 
for.

Templates are all about code generation. They aren't implementing any kind of 
API or interface and they have no way to declare any kind of API in the same 
sense that D interfaces do. A D interface (or Java or C# or whatever) gives an 
_exact_ set of functions that a class implementing it must have in order to be 
of that interface type. You can then declare variables of that interface. 
templates, on the other hand, can use variable APIs, and you cannot declare 
variables of whatever "interface" their API represents. They're purely code 
generation.

And how would you declare an "interface" for a template to test whether a type 
implements it? It's variable with the type. There could be several different APIs 
which would satisfy a template. Sure you could try and declare multiple 
interfaces, but you'd get a combinatorial explosion of them if you're talking 
about very many different functions. It could get ugly fast - _especially_ with 
ranges and all the different variations that you have there. Instead, all you 
have to do is create template or function which is used in a template constraint 
to check whether a given template argument will result in successful 
instantiation of that template.

And really, what's worse about isForwardRange!(T) than is(T: ForwardRange) as 
far as writing your programs that use them goes? You don't gain anything by 
having the more generic syntax of is(T : ForwardRange). You'd still have to 
declare interface ForwardRange somewhere. Instead of declaring an interface that 
T is forced to implement, instead you declare a function which checks that T has 
the functions that it's supposed to. It's not even really more work if you're 
the one writing isForwardRange!() vs interface ForwardRange. It's just an 
eponymous template instead of an interface.

The biggest difference is

Interfaces are for declaring types with a certain API and allow variables of 
that type which use polymorphism.

Templates with template constraints generate code given a particular set of 
template arguments as long as those template arguments result in the template's 
constraint being true. There is no type declared or polymorphism involved.

- Jonathan M Davis


More information about the Digitalmars-d mailing list