Compile-time Interfaces

Kapps Kapps at NotValidEmail.com
Sat Nov 26 16:40:08 PST 2011


One of the great things about D is the ability to do so much work at 
compile-time, including static verification. An example is the amount of 
constraints available for templates, static ifs, etc. But at some point, 
it starts getting very problematic to just figure out what something 
does, and what something can do. An example for this, is ranges. They 
can be very confusing, and you don't know what they can do without 
actually going and looking up the exact definition of them. Even then, 
you have to know that the templated type a function expects is a range. 
Again, very confusing, and arguably messy. Finally, even now that you 
know what methods you can call on this mysterious type T, and you see 
that there's a clause isInputRange!(T) on the method, your IDE still has 
no clue about any of these things making it impossible to have 
semi-decent code completion for that type.

Which brings in, compile-time interfaces. It seems like a natural thing 
to include when you have the above tools. Instead of having a method 
such as:
auto DoSomething(T)(T Data) if(isInputRange!(T)) { }
You could simply do:
auto DoSomething(Range Data) { }
where Range is defined as:
enum interface Range {
	void popFront() const;
	@property bool empty() const;
	@property auto front();
}
Much nicer than this very confusing looking statement (taken from 
std.range):
template isInputRange(R)
{
     enum bool isInputRange = is(typeof(
     {
         R r;              // can define a range object
         if (r.empty) {}   // can test for empty
         r.popFront();     // can invoke popFront()
         auto h = r.front; // can get the front of the range
     }()));
}
And then instead of returning auto, you could return Range if you wanted 
to, or OutputRange, etc. This gives much more info by just looking at 
the signature of the method, as opposed to comb through the 
documentation to find out what this mysterious type is that it returns.

Another useful thing is that new types could now actually have it be 
statically verified that they implement this feature:
struct MyRange(T) {
	T popFront() const { }
	@property bool empty() const { }
	@property ref T front() { }
}
When you try to use this in a method that takes in a type T where 
isInputRange!(T), you would get a confusing message saying no suitable 
overloads found. If you had, this however:
struct MyRange(T) : (static|enum)? Range {
	T popFront() const { }
	@property bool empty() const { }
	@property ref T front() { }
}
You would see a message saying that it can't implement Range because 
popFront returns T instead of void. Not only that, but a good IDE will 
also offer to implement the Range signatures for you, like Visual Studio 
does for C#.

The main issue I can think of is when a method takes in multiple 
different types that implement the same static interface. An example:
Range Zip(Range First, Range Second);
Would First/Second be the same type? Does it matter? Should the compiler 
handle it? What about the return type?
An alternate way though would just be to (when there are multiple types 
of the same interface) force the use of:
Range Zip(R1 : Range, R2 : Range)(R1 First, R2) Second;
An IDE will still know that these are ranges. You still get the 
readability of it. You still get all the benefits of the ranges. You 
just have to make Zip a template method anyways.

The other issue I can think of is that this could potentially make 
someone believe that methods that take in / return a Range aren't 
template methods when they actually are. Of course, in order for that to 
matter, they have to realize in the first place that a template method 
creates multiple instances of the method while caring about the overhead 
that creates.

Overall though, I think that this would be a huge benefit to D's compile 
time capability (not to mention learning ranges), while incurring no 
run-time cost. It also makes it much nicer when your IDE now knows what 
the types you're working with are. There are already IDEs that can take 
advantage of the above benefits, and only more will come. Plus, it's 
much much nicer to just be able to look at the interface rather than 
figuring out the documentation for, say, a range (and many editors/ides 
will offer a go-to-definition to do this for you). Template types / 
figuring out what they are is the messiest thing in D at the moment 
(IMO), and this would be a nice way of solving it for many situations.

Thoughts?


More information about the Digitalmars-d mailing list