This is actually a very common thing in D. This is how ranges work. For example, the most basic type of range - the input range - should have 3 functions:
bool empty();
You can then use the std.range.isInputRange template functions to check if the type is valid. For example, the most basic overload of std.algorithm.find looks like
R find(alias pred = "a == b", R, E)(R haystack, E needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) { ... }
isInputRange!R true if R is a valid input range and is(typeof(binaryFun!pred(haystack.front, needle)) : bool) is true if pred accepts haystack.front and needle and returns a type that is implicitly converted to bool . Thus, this overload is based solely on the static printing of ducks.
As for isInputRange itself, it looks something like
template isInputRange(R) { enum bool isInputRange = is(typeof( { R r = void;
This is the template of the same name, so when it is used, it is replaced by a symbol with its name, which in this case is an enumeration of type bool . And that bool is true if the type of the expression is not void . typeof(x) results in void if the expression is invalid; otherwise it is a type of expression x . And is(y) leads to true if y not void . Thus, isInputRange will isInputRange out to be true if the code in the typeof expression is compiled, and false otherwise.
The expression in isInputRange verifies that you can declare a variable of type R that R has a member (be it a function, a variable, or something else) with the name empty , which can be used in the condition that R has a function called popFront that does not accepts arguments and that R has a front member that returns a value. This is the expected API from the input range, and the expression inside typeof will compile if R follows this API, and therefore isInputRange will be true for this type. Otherwise, it will be false .
Standard library
D has quite a few such templates of the same name (usually called traits) and uses them heavily in its template restrictions. std.traits , in particular, is quite a lot. So, if you want more examples of how such traits are written, you can look there (although some of them are quite complex). The internals of such traits are not always particularly good, but they encapsulate duck typing tests, so the template constraints are much cleaner and more understandable (they would be much, much uglier if such tests were inserted directly into them).
So, this is the normal approach for static duck printing in D. Usually you need to figure out how to spell correctly, but this is the standard way to do this, and it works. There were people who suggested trying to come up with something similar to your Implements!(S, Interface) proposal, but nothing had happened so far, and such an approach would actually be less flexible, which would make it poorly suitable for many ( although this could be done to work with the basic ones). Despite this, the approach I described here is currently the standard way to implement it.
Also, if you know little about ranges, I would suggest reading this .