Question regarding the type of structural jump expressions

In the documentation https://kotlinlang.org/docs/reference/returns.html, it is stated

Kotlin has three structural jump expressions:

  • return . By default returns from the nearest enclosing function or anonymous function.
  • break . Terminates the nearest enclosing loop.
  • continue . Proceeds to the next step of the nearest enclosing loop.

All of these expressions can be used as part of larger expressions:

val s = person.name ?: return

The type of these expressions is the Nothing type.

I’m a bit confused, since a function with a blank return statement would return Unit, not Nothing. What am I missing?

Those aren’t really related, return means return Unit.

The expression return <whatever> has type Nothing.

If in a function you encounter return <whatever> then that function is returing <whatever> and hence, the function as a whole has return type the type of <whatever>.

I think you’re making confusion between the function return type and the type of the return expression itself.

1 Like

You don’t really have to worry about the type of the return expression. It’s a fancy trick that allows return to be used inside of larger statements.
Compare it to java: return in java is a statement and has no type. Therefor you can’t write

// java
Foo foo = bar != null ? bar : return

Because return has no type the expression bar != null ? bar : return also has no type and can’t be assigned to foo.
In kotlin however return has type Nothing which can be assigend to a value of every other type. Therefor you can write

val foo = someNullableFoo() ?: return

which has the type

val foo: Foo = Foo or Nothing

Oh I see, so they are two different things: the function’s return type is different from the type of the return expression itself (which is Nothing).

So unlike in functions, here

val s = person.name ?: return

if person.name is null, foo would be of type Nothing

Is there a reason though, that in Kotlin return, break and continue are expressions (which has type Nothing)? In the cases of if or when I could see that they could be useful as an expression, but for these it’s hard for me to see how they are useful or meaningful.

No it wouldn’t. Maybe my example above wasn’t perfect. You can’t just evaluate a type at runtime. This is all done at compile time. In your example s will have the same type as person.name.
Every expression has only one type which will be determined at compile time. For some expressions this is constant, eg. return expression is always of type Nothing and a string literal "foo" will always be of type String. For other expressions this has to be determined by the compiler. a + b for example depends on the types of a and b. Same is true for name ?: return. name has one type lets use String and return has the type Nothing. I don’t think this is stated anywhere (since this is going into implementation details) but the compiler will have some sort of rule that state: “The type of an elvis operator will be the most specific supertype of both arguments”. This means the compiler will now look for the most specific supertype of String and Nothing. Since Nothing is a subtype of every other type this will be String. Therefor the expression name ?: return will be of type String.

Well there are pretty much 2 options. Either return is a statement or an expression.
If it’s a statement it doesn’t have a type, but it also couldn’t be used as part of another expression.
So val n = name ?: return would be invalid since return is a statement and can’t be used inside of a larger expression.
I guess you could add special cases for return, etc but that would just make the compiler far more complicated and would probably just introduce bugs.
The other option is that return is an expression. In that case it can be used in any other larger expression but it also needs a type. return will never actually “return” a value to the expression it is used in (it will return from the function instead) and you want to use it in any expression regardless of what type the expression requires (you might want to use return for both Int, String, etc).
Those 2 criteria are coverd by a special kind of type called a Bottom Type. In kotlin this bottom type is called Nothing.

The most common use cases for return as an expression is the one above (val foo = bar ?: return)


Of cause all of this also applies to break and continue, as well as throw which is a 4th expression that is of type Nothing.


For more uses of the Nothing type in kotlin you should read
https://kotlinlang.org/docs/reference/exceptions.html#the-nothing-type
Otherwise I wouldn’t worry much about it. As I said earlier most of this is just some nice way of defining those expressions to make the implementation easier (in the kotlin compiler).

2 Likes

In type theory this type is often called bottom (subtype of all types).

Being able to use return, break, throw within expressions that are compatible to any type is pretty much the only reason I need. It is a very powerful concept, enabling patterns shown in previous posts.

The language guarantees inherently that values of type nothing will never be accessed by code. The programm can never reach a line of code that accesses a value of type nothing.

1 Like

In Kotlin the Nothing type can also be used as generic argument with some very interesting consequences, of course still having the rule that you can’t really produce a value with the type (mostly).