22

If I have a function that never returns (it contains an infinite loop) how do I declare/communicate that it will never return to the user of the function? The user does not see the source code, only the function signature and the documentation.

So the user will know to call my function in a new thread else his program will be blocked forever and the code below my function will never get executed.

Example function:

public void Foo()
{
    while (true)
    {
        _player.Play("scary_sound.wav");
        Thread.Sleep(TimeSpan.FromMinutes(5));
    }
}

Should I declare that as part of the documentation?

/// <summary>Plays scary sounds!</summary>
/// <remarks>This function never returns. Call it on a new thread.</remarks>
public void Foo()

Or name it with a "Run" prefix and "Loop" suffix or something?

public void RunFooLoop()

What ways are there to declare/communicate this? Any common practices? Any examples?

23
  • 17
    C has had a _Noreturn keyword since C11, but it's one of the few languages that do. Commented Aug 19, 2020 at 13:13
  • 8
    Kotlin also has the Nothing type, which can be used for exactly this.
    – Andy
    Commented Aug 19, 2020 at 13:37
  • 6
    At least with Kotlin's nothing and its most popular IDE, there's a static analysis immediately warning you when you invoke some code after calling a method with the Nothing return type, telling you that the code will never be reached. Unfortunately, this is not checked at compile time.
    – Andy
    Commented Aug 19, 2020 at 14:14
  • 5
    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 and throw (though, throw is not a function but a language intrinsic). This means that you can use throw in a function and the compiler can use it to do more complex flow analysis. For example, doing if(x == null) throw new Exc("x is null") can be replaced by if(x==null) error("x is null" assuming error returns Nothing. Commented Aug 19, 2020 at 21:34
  • 6
    Rust also has a Never type, denoted as ! for some obscure reason.
    – Thomas
    Commented Aug 20, 2020 at 12:44

9 Answers 9

64

This is one of those cases where infinite loops are so predominantly a bad idea that there's no real support/convention around using them.

Disclaimer
My experience is with C# (as is your code example, seems to be), but I would think that this is language-agnostic, unless there are languages which automatically wrap method calls in separate threads.

Based on the code you posted, what you've created here is a honeytrap. You can call the method any time you like, but you can never leave (I couldn't resist)

What you have here is the equivalent of asking your local government what signs you need to put up in your garden to tell people that you buried mines in your garden. The answer is that you shouldn't be burying mines in your garden.

The only practical use case for an infinite loop is for an application whose runtime process will be killed by the user (e.g. ping 8.8.8.8 -t is such an example). This is most commonly encountered in always-on services that are expected to run indefinitely.

However, playing a sound isn't something that you want your application to hang on. It's something you want to have happen concurrently. Therefore, you should design your code to work with it.

In essence, you should make a class that behaves like a media player. Maybe for you it's enough if this media player only has a start button with no way of ever turning it off again. I'll leave that up to you to decide.

But the play button click event is not an infinite loop. The sound should keep playing, but the click event should return.

This function never returns. Call it on a new thread.

Instead of pushing that responsibility to your consumer, just develop a class that, when called creates this thread and starts playing the sound.

This isn't a matter of "how do I tell my consumer that he should do this". It's significantly easier to do it for them. It promotes code reuse, lowers the change of bugs and unexpected behavior, and the documentation becomes easier to write as well.


Also, as an aside, you may want to look into using async/tasks instead of threads. It helps keep down the amount of active threads. But that's a different discussion.

19
  • 13
    @Fred: If you presume that the infinite loop is warranted here (which I'm willing to believe you on), it's better to prevent people getting stuck on it (by writing your own thread-spawning logic around it and having your consumers use that) instead of trying to decide on the phrasing used on the warning signs (= wondering how to clearly document it). Your question is built on the idea that you can just push the effort of safely handling your class onto the consumer, but I disagree and feel that you should make things safe yourself. You expect your car to come with seat belts and airbags, no?
    – Flater
    Commented Aug 19, 2020 at 14:22
  • 9
    On the flip side, there are so many different threading mechanisms in some languages, it might not be appropriate to "force" the choice of a particular one. I see value in having both: have a convenience method that spawns the thread/queue/channel or whatever else is appropriate in your language, have it call another method does the loop and never returns, but make that method also public in case people have other uses for it. Alternatively, make your method only do one "iteration", and have the use control it themselves. See also: developer.apple.com/documentation/foundation/runloop
    – Alexander
    Commented Aug 20, 2020 at 1:27
  • 5
    What strikes me the most as bad here is that he is making decisions for the application programmer. No doubt with the intent to be helpful but this isn't helpful. If the API user wants to play the sound repeatedly or do whatever repeatedly, he can do so in whatever way he sees fit. I would be cursing an API as described. "What was this idiot thinking? This is utterly useless". Commented Aug 20, 2020 at 5:15
  • 11
    -1 from me. Your comment about concurrency is absolutely valid, but it's quite orthogonal to the question. Indeed, an explicit never-return type is quite a good way of indicating to somebody using such a library function that it may be a good idea to wrap this call in a thread fork, without forcing them to use concurrency (which might be counterproductive overkill for something really running indefinitely on an embedded device with scarce resources). Commented Aug 20, 2020 at 12:11
  • 6
    There are some situations, such as in systems programming, where you absolutely do need an infinite loop or a function that otherwise never returns, and absolutely do need to call it without spinning off a thread. This includes, for instance, the Rust panic handler, which is defined when there isn't a standard library, called when the app panics, and is expected to take over the process. Calls like exit() are declared as never returning for a similar reason.
    – TheHans255
    Commented Aug 20, 2020 at 14:15
45

This depends on the type system and the language.

Many languages have a type for this, for example never in TypeScript, Nothing in Scala and Kotlin, or Void in Haskell.

C has a _Noreturn function specifier keyword.

In languages that support annotations (e.g. Java, C#), you could create a custom annotation that communicates your intent. Some IDE vendors or static analyzers might already have invented one for you.

Otherwise, documentation is the best thing you can do.

A type such as this is often a bottom type, meaning a type that is a subtype of every other type, and it is a type that can never have any instance. (This is called an "uninhabited" type.) Since it cannot have any instances, declaring the return type of a function as this type clearly communicates that the function never returns, since there is nothing it could return.

16
  • 2
    Yes, for most languages, documentation, convention and consistency should be the way how to "solve" this (e.g. by using a specific method suffix). For other languages actually supporting a mechanism to represent a method which never terminates, such mechanism should be used.
    – Andy
    Commented Aug 19, 2020 at 13:42
  • 22
    @Fred void at least in C, C++, and many similar languages just means the function doesn't return a value, but provides no indication on whether or not the function actually returns. A bottom type is a type that cannot be instantiated. A function that returns a bottom type cannot return without providing an instance of the bottom type, but since bottom cannot be instantiated, it must diverge, i.e. loop forever, throw an exception, power off the machine, etc.
    – 8bittree
    Commented Aug 19, 2020 at 14:21
  • 11
    @Fred: void signifies that a function does return, but doesn't return a useful value. never signifies that a function doesn't return. The equivalent to void in a more expressive type system would be a unit type, i.e. a type which has only one instance and no useful operations. Commented Aug 19, 2020 at 15:05
  • 4
    Haskell's is called Void. A fairly standard name is "empty type", which doesn't imply subtyping like "bottom type". C (and friends)'s void can be seen as having a single value (therefore no information; if you know something is void you know its value), that you just can't name. The identity of that single value is not important. In Java/Scala, the Null type (which is unnamed in Java) contains only the value null, but it's different from Scala's Unit, which has a different singleton value. "Jump type" seems... too specific? Maybe "unreachable".
    – HTNW
    Commented Aug 19, 2020 at 22:39
  • 2
    @Steve: there is standardization, mostly. Both the type and the value for a function that returns no useful value is almost universally called unit. It is only a small number of languages that deviate from that and call it void. Although note that the concept of void in e.g. C is very different, because it is not actually a type, it is more of a marker. In particular, in languages like Scala, there are no statements, there are only expressions. What would be a statement in C or Java is still an expression in Scala, just one whose value is unit. Commented Aug 20, 2020 at 0:39
12

I will answer from the point of view of C++, which has not been addressed in other answer at the time of writing.

Since C++11, there is an attribute, noreturn, that you can use to signal that a function will never actually give back control to the caller.

Before C++11, each compiler could decide to have such an extension (for example, GCC on this page you can find the description of the attribute used for this)

In embedded systems, I have seen such functions used as traps when an error or fault is detected, so the the processor can keep running and the developer can debug the problem. It might be useful in production too, but I've not seen such a use yet (and I guess it depends on the application.)

6

Naming

Or name it with a "Run" prefix and "Loop" suffix or something?

Instead of "Run" or "Loop", I'd prefer "Forever" here. As in, ForeverFoo() or FooForever().

There's a readability cost to long identifiers, but for rarely used ones this cost is negligible. I suspect the clarity outweighs the loss of brevity in this case. Do explain it in the documentation though, just to be explicit.

Factor out the pattern

If you have multiple of these infinite functions, consider factoring out the forever pattern to its own function/template/macro. Depending on your language, this might be tricky if Foo() requires arguments, but it would definitely make things cleaner and more natural.

In Python, for example, I might do something like this:

import time

def forever(func, *args, sleep_time=0, **kwargs):
    while True:
        func(*args, **kwargs)
        time.sleep(sleep_time)

forever(print, 'hi', end='#\n', sleep_time=1)

Then you might end up with a call like Forever(Foo), which is both readable and DRY.

This approach also lends itself well to other wrappers, for example Repeat() to play your scary sound a fixed number of times.

4

Provide low-level access and an expected-use wrapper

This is a frustrating bug waiting to happen. However, some users will want to control their own threading model. You can get the best of both worlds by creating a low-level function:

/** 
 * Play scary sounds in an infinite loop which never returns. 
 *  
 * See `PlayScarySounds` for a function which doesn't hijack the
 * current thread.
 */ 
public void PlayScarySoundsForever() {
    while (True) {
        _player.Play("scary_sound.wav");
        Thread.Sleep(TimeSpan.FromMinutes(5));
    }
}

And a wrapper function with your expected "spawn new thread" use case:

/** 
 * Spawn a new thread which plays scary sounds in an infinite loop.
 *
 * Wrapper around `PlayScarySoundForever`. 
 */
public void PlayScarySounds() {
    Thread.Spawn(() -> PlayScarySoundsForever());
}

Users might skim past the documentation, but they're less likely to skim over the list of methods and take note of a pattern there. The important part is that you give the "less scary" function the "less scary" name, since some users will still look at the method names and pick the one that looks most fitting.

Of course, there's also the question of "do you really not want to have some way of terminating this?" But that's, I think, a different question.

1
  • Yes, it would be nice to be able to terminate this. In .NET this is possible by passing in a CancellationToken as a argument. I like how the comments of both methods reference each other.
    – Fred
    Commented Aug 22, 2020 at 17:55
1

Swift has types "Void" and "Never". "Void" has only one value (and needs 0 bits to store it). "Never" has zero values. Therefore a function cannot return a value of type "Never". Therefore a function declared to return "Never" cannot return.

1

tl;dr Use asynchronous coding if you want asynchronous operation. Don't write sequential code unless:

  1. you actually want things to happen in the specific order; or/and

  2. 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:

  1. 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.

  2. 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:

  1. We want sequential operation.

  2. 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.

4
  • What about if it is a listener loop that listens for incoming connections on a port. The function could be declared as asynchronous but it would run forever so you cant do other things unless you run it on a different thread.
    – Fred
    Commented Aug 20, 2020 at 9:28
  • @Fred: Are you imagining a listener that loops over polling for new messages and sleeping until the next poll? Because that'd seem to be a sequential operation. (I mixed "sequential" and "synchronous" a bit above in an attempt to keep it short.)
    – Nat
    Commented Aug 20, 2020 at 9:31
  • no it doesn't need to sleep it can await reading new messages or using a blocking receive call. But since it listens for new connections for as long as the application is running, you probably want to do that on a different thread than the main thread.
    – Fred
    Commented Aug 20, 2020 at 9:35
  • @Fred: If it blocks, then it ought to be called Listen(), blocking until it's done listening (because blocking-until-completion is what sequential logic is all about). If it doesn't block the calling thread, then it ought to be called BeginListening(), blocking until it's done beginning the listening process.
    – Nat
    Commented Aug 20, 2020 at 9:38
1

Don't make a function that never returns. Don't ask the caller to put that function on its own thread.

Put the logic on its own thread within the function. Then name it appropriately for whatever it does.

For example, in C#:

public void PlaySounds()
{
    new Thread(() =>
    {
        while (true)
        {
            _player.Play("scary_sound.wav");
            Thread.Sleep(TimeSpan.FromMinutes(5));
        }
    }).Start();
}

However, as stated in other answers, an infinite loop is not the ideal way to go. Especially in this case, since the thread will never be killed. So maybe something like this would be more appropriate:

private static bool playSounds;
public void PlaySounds()
{
    playSounds = true;
    new Thread(() =>
    {
        while (playSounds)
        {
            _player.Play("scary_sound.wav");
            Thread.Sleep(TimeSpan.FromMinutes(5));
        }
    }).Start();
}

public void StopSounds()
{
    playSounds = false;
}
2
  • There are non-returning functions like "assert", "fatalerror", "longjmp" etc.
    – gnasher729
    Commented Aug 23, 2020 at 10:05
  • @gnasher729 semantics :)
    – Evorlor
    Commented Aug 23, 2020 at 13:07
0

In Python, you can use typing.NoReturn to indicate that a function never returns. e.g.

from typing import NoReturn

def stop() -> NoReturn:
    raise RuntimeError('no way')

Not the answer you're looking for? Browse other questions tagged or ask your own question.