tl;dr– Use asynchronous coding if you want asynchronous operation. Don't write sequential code unless:
you actually want things to happen in the specific order; or/and
you're confident that the earlier steps will return quickly enough for it to work out.
If you follow this, then it doesn't matter if a method returns because there's no practical distinction between non-returning methods and other types of methods that don't reliably return quickly (e.g., long-running methods and conditionally-returning methods).
Don't worry about if a method returns.
Programmers shouldn't be worried about stuff like if a method will ever return.
When you write procedural code, you're specifying a sequence of things that should happen. You shouldn't worry about if a method will ever return because it shouldn't matter; you don't want things after the method to happen before that method's done.
For example, if you write
PlaySounds();
SendEmail("Hey, I heard all the sounds!");
, it should be understood that the email won't send until the sounds are done playing.
By contrast,
BeginPlayingSounds();
SendEmail("Hey, I've begun listening to the sounds!");
will allow the email to be sent once sounds have started to play (which can mean different things), even if the sounds go on forever.
Discussion: False synchronicity as a performance hack.
Say you want to calculate both x*x
and y*y
.
Conceptually, unless you want these things to happen in a specific order, you shouldn't specify them as happening in a specific order.
Practically, these are very fast operations, so it's not usually worth the verbosity or computational overhead to write them as asynchronous. Modern compilers and CPU's may try to reorder such operations anyway.
In other words, while it's conceptually wrong to write non-synchronous logic as synchronous, it's often very practical to do so.
This disconnect between intent and practical coding is a deeper problem in modern programming practice. We're hesitant to do something like adopt conventions for non-returning methods because those wouldn't really fix the underlying problem.
For working programmers today, I'd think:
By default, use asynchronous coding whenever you don't actually require things to happen sequentially. Use sequential only if you actually want things to happen sequentially.
As a performance/brevity hack, reduce asynchronous code to falsely synchronous code according to your best judgement.
Switch focus to qualifying if methods will return quickly.
As discussed above, there're two reasons to write synchronous code:
We want sequential operation.
As a hack to dodge the verbosity/overhead of asynchronous code/execution.
So if you're going to call a method, the big thing you want to know is if it'll return quickly. Because if it does, then you might use falsely synchronous code.
Non-returning methods are just a subset of methods that don't necessarily return quickly. As such, they're not particularly interesting to programming practice.
_Noreturn
keyword since C11, but it's one of the few languages that do.Nothing
type, which can be used for exactly this.Nothing
return type, telling you that the code will never be reached. Unfortunately, this is not checked at compile time.Nothing
(in Kotlin) is actually much better then you guys say!Nothing
represents a value that has 0 instances. Therefore, if a function returns Nothing, it must never return. There are two main standard library functions that return Nothing:exitProcess
andthrow
(though, throw is not a function but a language intrinsic). This means that you can usethrow
in a function and the compiler can use it to do more complex flow analysis. For example, doingif(x == null) throw new Exc("x is null")
can be replaced byif(x==null) error("x is null"
assumingerror
returnsNothing
.!
for some obscure reason.