Thread overview
Flagging special conditions on return from a function call
Jun 23
Denis
Jun 23
Denis
Jun 24
Denis
June 23
Is there a preferred idiom in D for flagging special conditions when returning from a function call? Here "special conditions" refers to expected situations (e.g. reaching the end of something, like EOF) rather than outright errors (so exception-try-catch is inappropriate).

I've come across many ways for a function to return both a value and flag, including:

(1) Assign an unused value for the flag (e.g. -1 when the function returns an int), and return the combined value/flag.
(2) Return a tuple with the value and the flag
(3) Return a struct or tuple with named value and flag members
(4) Set the function return value normally, and put the flag in an "out" variable passed as an argument to the function
(5) Return the flag, and put the value in an "out" variable passed to the function (i.e. the reverse of #4)
(6) Use two separate functions, one that returns the value, and another that can be called afterwards to check the flag (like eof(), for example)
(7) Use a side effect and set a designated global variable

I'm sure there are others.

* Is there a preferred approach?
* Which ones are discouraged?
* General recommendations or guidelines?

If there is a best practice, I'd rather learn it sooner than many lines of code later.

(In the interest of brevity, I'll limit my own comments in this post to the following: In the past, I've tried to adhere to KISS. This means that I would generally prefer #1. But often it isn't possible to combine the value and flag that way, in which case one of the other methods must be used.)
June 23
On Tuesday, 23 June 2020 at 04:01:45 UTC, Denis wrote:
> (1) Assign an unused value for the flag (e.g. -1 when the function returns an int), and return the combined value/flag.

This happens in some Phobos algorithms, and might be the most common on this list.


> (2) Return a tuple with the value and the flag
> (3) Return a struct or tuple with named value and flag members

Would certainly work, but I don't think it's common in D.


> (4) Set the function return value normally, and put the flag in an "out" variable passed as an argument to the function
> (5) Return the flag, and put the value in an "out" variable passed to the function (i.e. the reverse of #4)

Both of these happen. I don't know which is more common. In C# these are probably the most common way (except for exceptions) to signal these cases.


> (6) Use two separate functions, one that returns the value, and another that can be called afterwards to check the flag (like eof(), for example)


> (7) Use a side effect and set a designated global variable

Global variables are frowned upon, so probably not this. :p


One thing I feel is missing here (perhaps because std.variant.Algebraic is egregious):

(8) Return a Maybe!T or Algebraic!(T, ErrorCode)

It's what I personally would prefer, but I have only rarely seen it in D code. Given a properly written Maybe, this could enforce proper handling of the error case, either by throwing on trying to get at Maybe!T.getValue when it's holding None, or by presenting an interface that only compiles when both cases are covered, like fun().match((T t) => t, () => Error()).

--
  Simen
June 23
Perhaps this thread would have been better titled "Returning a value and a status", and the question phrased as "What are your preferred techniques?".

I'm planning to port some of my programs to D, so I'd like to standardize on one or two techniques for handling this very common situation, in a way that suits the language. I'd appreciate hearing from others about what you do.

---

On Tuesday, 23 June 2020 at 06:52:29 UTC, Simen Kjærås wrote:
> On Tuesday, 23 June 2020 at 04:01:45 UTC, Denis wrote:

[...]

>> (4) Set the function return value normally, and put the flag in an "out" variable passed as an argument to the function
>> (5) Return the flag, and put the value in an "out" variable passed to the function (i.e. the reverse of #4)
>
> Both of these happen. I don't know which is more common. In C# these are probably the most common way (except for exceptions) to signal these cases.

Good to know. Both of these techniques (or something similar) are used in Perl too. The choice seemed to depend on which one the author thought would be more useful as the return.

[...]

> by presenting an interface that only compiles when both cases are covered, like fun().match((T t) => t, () => Error()).

A complete solution wrapped in a tidy package -- I like it.

Thanks for sharing.
June 23
On Tuesday, 23 June 2020 at 16:14:20 UTC, Denis wrote:
>> by presenting an interface that only compiles when both cases are covered, like fun().match((T t) => t, () => Error()).
>
> A complete solution wrapped in a tidy package -- I like it.
>
> Thanks for sharing.

If you're open to using Dub packages, I'd recommend sumtype [1] as an alternative to the standard-library Algebraic. It has much better compatibility with things like function attributes (@safe, nothrow, etc), mutability qualifiers, CTFE, and betterC, and also produces substantially more efficient code [2].

[1] https://code.dlang.org/packages/sumtype
[2] https://pbackus.github.io/blog/beating-stdvisit-without-really-trying.html
June 24
On Tuesday, 23 June 2020 at 21:34:25 UTC, Paul Backus wrote:

> If you're open to using Dub packages [...]

Because this is going to be used in almost every program I write, I need to eliminate outside dependencies as an option. Nonetheless, thanks for this suggestion.

> [2] https://pbackus.github.io/blog/beating-stdvisit-without-really-trying.html

I did read your blog post: that's an impressive result. Good to know that well-written D code can be competitive with C.

Denis