I agree with your point, but for the sake of the audience who doesn't know D I think this example is misleading, as one could take the "int i" parameter as a runtime one, while it's actually a compile time one (the equivalent of C++ non-type template parameter). If you instantiate the function with 0 as a compile-time parameter, it is a function that returns int; otherwise it's a function that returns string. It is never a function that can return int or string.
I still think the best option is let the author describe it in ddoc, as the semantic meaning can be much easier to convey that way.
If the docs are filled with this, then D is certainly coming off my list of langs to look at.
For functions that can return different types, I think interfaces or union types would be more helpful (not sure if D supports either though).
There's a wanting implementation of a sumtype in the standard library (https://dlang.org/phobos/std_variant.html#.Algebraic), and a much better one as a package: https://code.dlang.org/packages/sumtype
Template declarations in D take 2 parameter lists. The first is the template parameters the second the runtime parameter: in auto whatDoesItReturn(int i)() { static if (i == 0) { return int.init; } else { return string.init; } }
we have (int i) as template parameter and () as an empty runtime parameter. In C++ syntax whatDoesItReturn<int i>()
at instanciation the syntax is different:
whatDoesItReturn!0() will instantiate a function returning an int
whatDoesItReturn!42() will instantiate a function returning a string.
The docs on static if may shed some more light: https://dlang.org/spec/version.html#staticif
Template arguments need to be known at compile time, and the extra set of parens is how templates parameters are declared in D.
Another reason is the (ironically) dynamic nature of a return type. E.g.
auto whatDoesItReturn(int i)() { static if (i == 0) { return int.init; } else { return string.init; } }
Template code can do that quite easily and then you don't have a choice but to write auto as the return value.
What would be fantastic if the documentation could be given access to the the compiler's return type inference, so that it could document the auto returns with a little more info.
Another way useful approach would be to implement protocols like in swift, or traits like in scala/rust/others, signatures in ml, etc. Then you would be able to define the interface of what a function returns.