Please do not use 'auto' return types without thoroughly describing the interface

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Dec 27 23:06:57 UTC 2017


On Tue, Dec 26, 2017 at 01:50:06AM +0000, Neia Neutuladh via Digitalmars-d wrote:
> On Monday, 25 December 2017 at 22:48:39 UTC, H. S. Teoh wrote:
> > The exact type does not and should not need to be known by user
> > code.
> 
> It's a public type reported by some of the documentation but not other
> parts. Its capabilities are given vaguely in the docs for the
> functions that return it.
> 
> This is a documentation problem, but concrete return types will put a
> hard limit on how bad the documentation situation can be.

Then the documentation needs to be fixed.  Resorting to concrete return
types seems to me to be a pessimistic approach, when we should rather be
improving the code / docs.


> > It's an implementation detail.
> 
> The fact that there is a named type involved is not shocking. If there
> is not a named type, we can use something like:
> 
>   public alias Regex(Char) = typeof(regex([Char.init]));
> 
> I can add it in my own code too. Pretty much no chance of breakage.
> 
> The fact that two functions return values of the same type is perhaps
> an implementation detail, but there's no way to ensure you don't break
> user code that way besides using a unique type for each (hello,
> std.typecons.Typedef).

If more than one function is returning the same type, then that's
reasonable grounds to ask for a named return type.

OTOH, there's the case where the API may reasonably return distinct
types, but just so happens that the present implementation reuses the
same type, in which case user code should not rely on the return types
being compatible with each other.  Your example below of matchFirst vs.
matchAll could, arguably, be an example of this category, since
conceivably, matchFirst could return a type that encapsulates a single
match, whereas matchAll could return a range of such matches.  Then
potentially a future version of the library could use different return
types, even if the current implementation does reuse the same type.
Assuming that the return types are compatible is an iffy proposition at
best.

Keeping the return types opaque helps to discourage this sort of
misplaced assumption in user code.


> Like I might read the docs and, quite reasonably, write:
> 
>   auto capture = needFirst ? matchFirst(str, regex) : matchAll(str,
> regex).skip(3).front;
>   auto process = asShell ? spawnShell("echo hi") :
> spawnProcess(["/bin/echo", "hi"]);

I think you're missing some context there, I'm not sure what the second
line of code has to do with `capture` in the first line. Care to
clarify?


> And that's suddenly going to stop compiling some day. Maybe. Except
> that would be bonkers because nobody's going to write two different
> `Captures` structs to be used in nearly identical circumstances. It
> would be pointless work to have two different types with the same
> interface for processes.

Not necessarily, if matchFirst returns a single Match type, say, and
matchAll returns a range of Match elements. Then you could very well
have different, incompatible return types here.


> The fact that it's somehow absolutely essential to maintain this
> option in all current cases, but not so essential as to motivate
> anyone to deprecate public types for anything or use Typedef
> everywhere, is kind of worrying.

I wouldn't say it's *absolutely essential*, just that imposing the least
amount of constraints on the code (including possible future changes) is
a desirable thing to have.


T

-- 
If you're not part of the solution, you're part of the precipitate.


More information about the Digitalmars-d mailing list