From a7e5e1adf9709ba4a3f95cc361d648dea5634eb2 Mon Sep 17 00:00:00 2001 From: vmishenev Date: Fri, 21 Jul 2023 00:26:08 +0300 Subject: [PATCH 1/6] Add proposal for infinite loop --- proposals/infinite-loop.md | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 proposals/infinite-loop.md diff --git a/proposals/infinite-loop.md b/proposals/infinite-loop.md new file mode 100644 index 000000000..ff290ad95 --- /dev/null +++ b/proposals/infinite-loop.md @@ -0,0 +1,82 @@ +# Infinite loop + +* **Type**: Design proposal +* **Author**: Vadim Mishenev, Roman Elizarov +* **Status**: In Progress +* **Prototype**: In Progress +* **Issue**: [KT-27970](https://youtrack.jetbrains.com/issue/KT-27970/Support-an-infinite-for-loop) + +## Summary + +An [infinite loop](https://en.wikipedia.org/wiki/Infinite_loop) (also known as a “while true”) is an idiom that is widely used in Kotlin programming. + +## Motivation / use cases + + +- Infinite loops are widely used to monitor user input or device activity. +The idiomatic approach to reading all the lines from the input stream until it is over (until readLine functions returns null): + + ```kotlin + while (true) { + val line = input.readLine() ?: break + // process line + } + + ``` +Or the while loop can be used for the main game frame which continues to get executed until the user or the game selects some other event. +- Infinite loops also appear quite often in concurrent programming with coroutines, because various concurrent background processes are often conveniently represented as an infinite loop doing something until cancelled. + + + + + + +The proposal is to support infinite loop via `while { ... }` statement without parameters or conditions, which seems to read nicely for the case of infinite loop, changing the motivating example to: + +```kotlin +while { + val line = input.readLine() ?: break + // process line +} + +``` +But it can look like that user forgot to write a condiftion. + + +In other languages: + +* Go (Golang) has `for { ... }` - loop. Probably `for` stems from `repeat forever`. + +* In Rust there is `loop { ... }`. + +* In C# there was a [proposal](https://github.com/dotnet/csharplang/issues/2475), but the discussion was shut down. + +and so on (Ada, Ruby, Fortran). + +Infinite loops can be implemented via a function (like `repeat` in StdLib), but it will not support `break`/`continue`. So this feature is shortest way to support it. + + +## Type inference + +Currently, infinite loops cannot be properly used inside scope functions. The following code does not compile due to type mismatch, since while is not an expression and the resulting type of run coerces to Unit: + +```kotlin +private fun foo(): Nothing = // ERROR: Type inference failed. Expected: Unit + run { + while (true) { + doSomething() + } + } +``` + +Infinite loop, on the other hand, shall be an expression of `Nothing` type (similar to `throw` to mark code locations that can never be reached) so the following code should compile: + +```kotlin +private fun foo(): Nothing = // ERROR: Type inference failed. Expected: Unit + run { + while { + doSomething() + } + } +``` +But an inifinite loop with `break` is an expression of `Unit` type. From 5128bd19df8c81b0e826eafcb633c61ab5b2353f Mon Sep 17 00:00:00 2001 From: vmishenev Date: Wed, 4 Oct 2023 22:26:52 +0300 Subject: [PATCH 2/6] Rewrite proposal for infinite loop --- proposals/infinite-loop.md | 203 +++++++++++++++++++++++++++++++------ 1 file changed, 171 insertions(+), 32 deletions(-) diff --git a/proposals/infinite-loop.md b/proposals/infinite-loop.md index ff290ad95..6e4b819e0 100644 --- a/proposals/infinite-loop.md +++ b/proposals/infinite-loop.md @@ -1,82 +1,221 @@ # Infinite loop * **Type**: Design proposal -* **Author**: Vadim Mishenev, Roman Elizarov +* **Author**: Vadim Mishenev +* **Contributors**: Roman Elizarov * **Status**: In Progress -* **Prototype**: In Progress +* **Prototype**: In Progress (inferring `Nothing` for `while(true)` is implemented) * **Issue**: [KT-27970](https://youtrack.jetbrains.com/issue/KT-27970/Support-an-infinite-for-loop) ## Summary -An [infinite loop](https://en.wikipedia.org/wiki/Infinite_loop) (also known as a “while true”) is an idiom that is widely used in Kotlin programming. -## Motivation / use cases +This KEEP introduces a new expression to indicate an [infinite loop](https://en.wikipedia.org/wiki/Infinite_loop) (also known as a “while true”) and inferring types for a such loop. +## Motivation -- Infinite loops are widely used to monitor user input or device activity. -The idiomatic approach to reading all the lines from the input stream until it is over (until readLine functions returns null): - - ```kotlin - while (true) { - val line = input.readLine() ?: break - // process line - } +Kotlin leads to be laconic programming language, and an infinite loop `while(true) { .. }` (rare `do { .. } while(true)`) might be expressed more concisely. +Besides, a dedicated expression makes an infinite loop immediately understandable. +This usually results in more readable code. - ``` -Or the while loop can be used for the main game frame which continues to get executed until the user or the game selects some other event. -- Infinite loops also appear quite often in concurrent programming with coroutines, because various concurrent background processes are often conveniently represented as an infinite loop doing something until cancelled. +### Use-cases +Infinite loops is an idiom that is widely used in Kotlin programming: +- Infinite loops are widely used to monitor user input or device activity. +The idiomatic approach to reading all the lines from the input stream until it is over (until `readLine` function returns null): +```kotlin +while(true) { + val line = input.readLine() ?: break + // process line +} +``` +Or the while loop can be used for the main game frame which continues to get executed until the user or the game selects some other event. +- Infinite loops also appear quite often in concurrent programming with coroutines, because various concurrent background processes are often conveniently represented as an infinite loop doing something until cancelled. -The proposal is to support infinite loop via `while { ... }` statement without parameters or conditions, which seems to read nicely for the case of infinite loop, changing the motivating example to: +- It is often used along with exit conditions / jump expressions at the middle of a body loop. +with `when` condition: ```kotlin -while { - val line = input.readLine() ?: break - // process line +while(true) { + when(await().resultCode) { + RESULT_OK -> break + RESULT_CANCELED -> { + finish() + return false + } + else -> continue + } } +``` +into a `try-catch` block: +```kotlin +while(true) { + try { + // repeated process + } catch(e: InterruptedException) { + break; + } +} ``` -But it can look like that user forgot to write a condiftion. -In other languages: +The list of use cases is not exhaustive. +In general, an infinite loop is a common form, and usual loops (`while`, `do-while`) with a condition can be rewritten with it and vice versa. + + +### Other languages * Go (Golang) has `for { ... }` - loop. Probably `for` stems from `repeat forever`. * In Rust there is `loop { ... }`. * In C# there was a [proposal](https://github.com/dotnet/csharplang/issues/2475), but the discussion was shut down. +Summary: The community does not want to encourage the use of infinite loops. In their opinion, it is better to have loops with a condition or flag that can explain a reason of termination. +`while{ .. }` looks like a user accidentally forgot to type the condition or accidentally removes it (say cut/paste instead of copy/paste) and ends up with valid code that represents an infinite loop. and so on (Ada, Ruby, Fortran). -Infinite loops can be implemented via a function (like `repeat` in StdLib), but it will not support `break`/`continue`. So this feature is shortest way to support it. +Remarkably, Rust, Go, Ruby do not have `do-while` loop. So infinite loop can be a way to write it in these languages. + + +## Design + +Syntax-wise, the proposal is to support infinite loop via the concise `for { ... }` statement (or any other keyword instead of `for`, see "Open questions" section) without parameters or conditions, which seems to read nicely for the case of infinite loop, changing the motivating example to: + +```kotlin +for { + val line = input.readLine() ?: break + // process line +} + +``` +Also, factoring this form both away from and back to having a condition is natural. + +## Statement or expression + +### Other languages + +In Golang infinite loop `for { ... }` is a statement. (see [The Go Language Specification: for clause](https://go.dev/ref/spec#ForClause)). +On the other hand, in Rust `loop { ... }` is an expression. (see [The Rust Reference: infinite loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops)) +A loop expression without an associated break expression has type `!`([Never type](https://doc.rust-lang.org/reference/types/never.html)) +Meanwhile, `break` expressions of Rust can have a [loop value](https://doc.rust-lang.org/reference/expressions/loop-expr.html#break-and-loop-values) only for infinite loops, e.g. -## Type inference +```rust +let result = loop { + if b > 10 { + break b; + } + let c = a + b; + a = b; + b = c; +}; +``` + +### Type inference in Kotlin -Currently, infinite loops cannot be properly used inside scope functions. The following code does not compile due to type mismatch, since while is not an expression and the resulting type of run coerces to Unit: +Currently, in Kotlin infinite loops cannot be properly used inside scope functions. The following code does not compile due to type mismatch, since `while` is not an expression and the resulting type of run coerces to Unit (see [KT-25023](https://youtrack.jetbrains.com/issue/KT-25023/Infinite-loops-in-lambdas-containing-returnlabel-dont-coerce-to-any-type)): ```kotlin -private fun foo(): Nothing = // ERROR: Type inference failed. Expected: Unit +private fun foo(): Nothing = // Should be compiled, but Error: Type mismatch: inferred type is Unit but Nothing was expected + run { while (true) { doSomething() } } ``` - -Infinite loop, on the other hand, shall be an expression of `Nothing` type (similar to `throw` to mark code locations that can never be reached) so the following code should compile: +Infinite loop shall be an expression of `Nothing` type (similar to `throw` to mark code locations that can never be reached) so this code compiles. +Meanwhile, the type should be `Unit` if a loop contains `break`. +Despite `return` breaking a loop, it does not make code after an infinite loop reachable so the expression type can be `Nothing`. It allows to compile the following code: ```kotlin -private fun foo(): Nothing = // ERROR: Type inference failed. Expected: Unit - run { - while { - doSomething() +val x = run { + while (true) { + return@run 1 // Error: Type mismatch: inferred type is Unit but Int was expected } } ``` -But an inifinite loop with `break` is an expression of `Unit` type. + +Also, it makes sense to make existing loops with a condition (`for`, `do-while`, `while`) expressions. But IDE can have an intention to transform loops with `true` condition into proposed infinity loops. + + +### Functions with expression body + + `return` is also frequently used to exit from an infinite loop, but `return`s are not allowed for functions with expression body in Kotlin, e.g. + +```kotlin +fun test() = while (true) { + return 42 // Error: Returns are not allowed for functions with expression body. Use block body in '{...}' + } +``` +This problem can be solved by `break` with a loop value like in Rust. But it deserves another proposal. + +## Feature versus stdlib function + +Infinite loops can be implemented via a function (like `repeat` in StdLib), but it will not support `break`/`continue`. So this feature is the shortest way to support it. +[KT-19748](https://youtrack.jetbrains.com/issue/KT-19748/Provide-some-sort-of-break-continue-like-mechanism-for-loop-like-functions) solves this problem for a such function and loop-like functions (`forEach`, `filter`, etc). But there are some disadvantages of this feature: + +- It seems to be very specified only for stdlib's functions. +- Existed local `return` can become misleading with a labelled `break`/`continue`. A user can expect a local `return` should exit a loop-like function at all. It might need to change the behavior of local `return` for loop-like function. + + +```kotlin + (0..9).forEach { + if (it == 0) return@forEach // currently, it has the same behavior as `continue@forEach` + println(it) + } +``` + + +- For a function of infinite loop inside a loop (`for`, `while`), it might require extra labels that make code verbose. +See [KEEP-326](https://github.com/Kotlin/KEEP/issues/326): +an unlabeled `break`/`continue` goes to the innermost enclosing block that is clearly marked with `for`, `do`, or `while` hard keywords: + +``` +for (elem in 1..10) { + (1..10).someInfiniteLoopFunction { + println(it) + if (it == 5) break@someInfiniteLoopFunction + } +} +``` + +- The implementation of the feature is more complicated than infinite loops. + + +## Open questions + +### Which keyword is used for infinite loops? + +Main candidates of the keyword: + +- `while { ... } ` +- `for { ... }` + The Golang (Go) uses it. Probably `for` is shortened to `forever`. +- `do { .. }` But `do {} while(..) {}` can be ambiguous so for a user. A user should check the end of a loop to differ `do-while` and `do` loops. +- `loop { ... }` + This keyword is used Rust for infinite loops. +- `repeat { ... }` + + +The plus of using words `while`, `do`, `for` is that they are existing keywords. Users are already familiar with their semantics. +Otherwise, introducing a new keyword increases syntax complexity of the language. +But it can look like that a user forgot to write a condition. +Also, the existing keywords `for` and `while` requres `{ ... }` for an infinite loop body. +Otherwise, for example, the following code becomes ambiguous: + +```kotlin +for (x in xs); +``` +It can be treated either as a for loop with a loop variable `x` and an empty body or as an infinite loop which evaluates expression `(x in xs)`. + + +The question requires UX research. + + From ca5030363baa21860430850519684a7e44b0c2c0 Mon Sep 17 00:00:00 2001 From: vmishenev Date: Mon, 16 Oct 2023 22:03:04 +0300 Subject: [PATCH 3/6] Enhance design section --- proposals/infinite-loop.md | 75 +++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/proposals/infinite-loop.md b/proposals/infinite-loop.md index 6e4b819e0..fe53a0aa9 100644 --- a/proposals/infinite-loop.md +++ b/proposals/infinite-loop.md @@ -4,23 +4,23 @@ * **Author**: Vadim Mishenev * **Contributors**: Roman Elizarov * **Status**: In Progress -* **Prototype**: In Progress (inferring `Nothing` for `while(true)` is implemented) +* **Prototype**: In Progress (inferring `Nothing` is implemented) * **Issue**: [KT-27970](https://youtrack.jetbrains.com/issue/KT-27970/Support-an-infinite-for-loop) ## Summary - -This KEEP introduces a new expression to indicate an [infinite loop](https://en.wikipedia.org/wiki/Infinite_loop) (also known as a “while true”) and inferring types for a such loop. +This KEEP introduces a new expression `for { ... }` to indicate an [infinite loop](https://en.wikipedia.org/wiki/Infinite_loop) (also known as a “while true”). ## Motivation -Kotlin leads to be laconic programming language, and an infinite loop `while(true) { .. }` (rare `do { .. } while(true)`) might be expressed more concisely. +Kotlin leads to be laconic programming language, and an infinite loop `while(true) { .. }` (rare `do { .. } while(true)`) might be expressed more concisely. Besides, a dedicated expression makes an infinite loop immediately understandable. This usually results in more readable code. ### Use-cases -Infinite loops is an idiom that is widely used in Kotlin programming: +Infinite loops is an idiom that is widely used in Kotlin programming. +In Kotlin the percentage of `while(true)` among all written `while` loops is 19% (in 1.2M repositories from the BigCode tool). - Infinite loops are widely used to monitor user input or device activity. The idiomatic approach to reading all the lines from the input stream until it is over (until `readLine` function returns null): @@ -85,16 +85,9 @@ Remarkably, Rust, Go, Ruby do not have `do-while` loop. So infinite loop can be ## Design -Syntax-wise, the proposal is to support infinite loop via the concise `for { ... }` statement (or any other keyword instead of `for`, see "Open questions" section) without parameters or conditions, which seems to read nicely for the case of infinite loop, changing the motivating example to: - -```kotlin -for { - val line = input.readLine() ?: break - // process line -} +The proposal is to support infinite loop via the concise `for { ... }` construction without parameters or conditions. The curly braces `{ ... }` are required. +It should be used as expression with the result type `Nothing`, but if a loop has a `break` expression, the result type of expression should be `Unit`. -``` -Also, factoring this form both away from and back to having a condition is natural. ## Statement or expression @@ -122,8 +115,7 @@ let result = loop { Currently, in Kotlin infinite loops cannot be properly used inside scope functions. The following code does not compile due to type mismatch, since `while` is not an expression and the resulting type of run coerces to Unit (see [KT-25023](https://youtrack.jetbrains.com/issue/KT-25023/Infinite-loops-in-lambdas-containing-returnlabel-dont-coerce-to-any-type)): ```kotlin -private fun foo(): Nothing = // Should be compiled, but Error: Type mismatch: inferred type is Unit but Nothing was expected - +fun foo(): Nothing = // Should be compiled, but Error: Type mismatch: inferred type is Unit but Nothing was expected run { while (true) { doSomething() @@ -141,8 +133,21 @@ val x = run { } } ``` +Moreover, the porpose infinite loop `for { ... }` with `Nothing`/`Unit` types is backwards compatible with old code. + +### Should existing loops in Kotlin be expresions? -Also, it makes sense to make existing loops with a condition (`for`, `do-while`, `while`) expressions. But IDE can have an intention to transform loops with `true` condition into proposed infinity loops. +IDE can have an intention to transform loops with `true` condition into proposed infinite loops. It could make sense to make existing loops with a condition (`do-while`, `while`) expressions. +But it breaks backward compatibility with already written code, for example: + +```kotlin +fun foo() = run { + while (true) { + doSomething() + } +} +``` +After this changing, `foo` will have `Nothing` type instead of `Unit`. Also, it will cause the compiler error `'Nothing' return type needs to be specified explicitly`. ### Functions with expression body @@ -162,7 +167,7 @@ Infinite loops can be implemented via a function (like `repeat` in StdLib), but [KT-19748](https://youtrack.jetbrains.com/issue/KT-19748/Provide-some-sort-of-break-continue-like-mechanism-for-loop-like-functions) solves this problem for a such function and loop-like functions (`forEach`, `filter`, etc). But there are some disadvantages of this feature: - It seems to be very specified only for stdlib's functions. -- Existed local `return` can become misleading with a labelled `break`/`continue`. A user can expect a local `return` should exit a loop-like function at all. It might need to change the behavior of local `return` for loop-like function. +- Existed local `return` can become misleading with a labelled `break`/`continue` expression. A user can expect a local `return` should exit a loop-like function at all. It might need to change the behavior of local `return` for loop-like function. ```kotlin @@ -189,26 +194,28 @@ for (elem in 1..10) { - The implementation of the feature is more complicated than infinite loops. -## Open questions - -### Which keyword is used for infinite loops? +## Which keyword is used for infinite loops? Main candidates of the keyword: -- `while { ... } ` +- `while { ... } ` - `for { ... }` - The Golang (Go) uses it. Probably `for` is shortened to `forever`. -- `do { .. }` But `do {} while(..) {}` can be ambiguous so for a user. A user should check the end of a loop to differ `do-while` and `do` loops. + The Golang (Go) uses it. Probably `for` is shortened to `forever`. +- `do { ... }` But `do {} while(...) {}` can be ambiguous so for a user. A user should check the end of a loop to differ `do-while` and `do` loops. - `loop { ... }` This keyword is used Rust for infinite loops. - `repeat { ... }` The plus of using words `while`, `do`, `for` is that they are existing keywords. Users are already familiar with their semantics. -Otherwise, introducing a new keyword increases syntax complexity of the language. -But it can look like that a user forgot to write a condition. -Also, the existing keywords `for` and `while` requres `{ ... }` for an infinite loop body. -Otherwise, for example, the following code becomes ambiguous: +Otherwise, introducing a new keyword increases syntax complexity of the language. +Also, it can break backward compatibility since old code can have a user function with the same name as the keyword. + +`while { ... } ` can look like that a user forgot to write a condition. At the same time, unlike othe languages, Kotlin does not have a free-form condition for `for` loop. + + +The existing keywords `for` and `while` require `{ ... }` for an infinite loop body. +Contrariwise, for example, the following code becomes ambiguous: ```kotlin for (x in xs); @@ -216,6 +223,16 @@ for (x in xs); It can be treated either as a for loop with a loop variable `x` and an empty body or as an infinite loop which evaluates expression `(x in xs)`. -The question requires UX research. +To sum up, `for` is the best choice for an infinite loop. It does not require intruduction a new keyword and do not have the problem with forgotten condition like `while`. +It seems to read nicely for the case of an infinite loop, changing the motivating example from `Use-cases` section to: + +```kotlin +for { + val line = input.readLine() ?: break + // process line +} + +``` +Also, factoring this form both away from and back to having a condition is natural. From 32097345e22a89fcb475fd5c9f27cc36eec3c80c Mon Sep 17 00:00:00 2001 From: vmishenev Date: Tue, 17 Oct 2023 16:52:51 +0300 Subject: [PATCH 4/6] Polish section about existing loops --- proposals/infinite-loop.md | 126 ++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/proposals/infinite-loop.md b/proposals/infinite-loop.md index fe53a0aa9..e0ca2b6dd 100644 --- a/proposals/infinite-loop.md +++ b/proposals/infinite-loop.md @@ -13,7 +13,7 @@ This KEEP introduces a new expression `for { ... }` to indicate an [infinite loo ## Motivation -Kotlin leads to be laconic programming language, and an infinite loop `while(true) { .. }` (rare `do { .. } while(true)`) might be expressed more concisely. +Kotlin leads to be laconic programming language, and an infinite loop `while(true) { ... }` (rare `do { ... } while(true)`) might be expressed more concisely. Besides, a dedicated expression makes an infinite loop immediately understandable. This usually results in more readable code. @@ -25,43 +25,44 @@ In Kotlin the percentage of `while(true)` among all written `while` loops is 19% - Infinite loops are widely used to monitor user input or device activity. The idiomatic approach to reading all the lines from the input stream until it is over (until `readLine` function returns null): -```kotlin -while(true) { - val line = input.readLine() ?: break - // process line -} + ```kotlin + while(true) { + val line = input.readLine() ?: break + // process line + } -``` -Or the while loop can be used for the main game frame which continues to get executed until the user or the game selects some other event. + ``` + Or the while loop can be used for the main game frame which continues to get executed until the user or the game selects some other event. - Infinite loops also appear quite often in concurrent programming with coroutines, because various concurrent background processes are often conveniently represented as an infinite loop doing something until cancelled. - It is often used along with exit conditions / jump expressions at the middle of a body loop. -with `when` condition: -```kotlin -while(true) { - when(await().resultCode) { - RESULT_OK -> break - RESULT_CANCELED -> { - finish() - return false - } - else -> continue - } -} -``` -into a `try-catch` block: - -```kotlin -while(true) { - try { - // repeated process - } catch(e: InterruptedException) { - break; - } -} -``` + with `when` condition: + + ```kotlin + while(true) { + when(await().resultCode) { + RESULT_OK -> break + RESULT_CANCELED -> { + finish() + return false + } + else -> continue + } + } + ``` + or inside `try-catch` block: + + ```kotlin + while(true) { + try { + // repeated process + } catch(e: InterruptedException) { + break; + } + } + ``` The list of use cases is not exhaustive. @@ -70,13 +71,17 @@ In general, an infinite loop is a common form, and usual loops (`while`, `do-whi ### Other languages -* Go (Golang) has `for { ... }` - loop. Probably `for` stems from `repeat forever`. +- Go (Golang) has `for { ... }` - loop. Probably `for` stems from `repeat forever`. + +- In Rust there is `loop { ... }`. -* In Rust there is `loop { ... }`. +- In C# there was a [proposal](https://github.com/dotnet/csharplang/issues/2475), but the discussion was shut down. + + **Summary** -* In C# there was a [proposal](https://github.com/dotnet/csharplang/issues/2475), but the discussion was shut down. -Summary: The community does not want to encourage the use of infinite loops. In their opinion, it is better to have loops with a condition or flag that can explain a reason of termination. -`while{ .. }` looks like a user accidentally forgot to type the condition or accidentally removes it (say cut/paste instead of copy/paste) and ends up with valid code that represents an infinite loop. + The community does not want to encourage the use of infinite loops. In their opinion, it is better to have loops with a condition or flag that can explain a reason of termination. + + `while{ .. }` looks like a user accidentally forgot to type the condition or accidentally removes it (say cut/paste instead of copy/paste) and ends up with valid code that represents an infinite loop. and so on (Ada, Ruby, Fortran). @@ -85,7 +90,7 @@ Remarkably, Rust, Go, Ruby do not have `do-while` loop. So infinite loop can be ## Design -The proposal is to support infinite loop via the concise `for { ... }` construction without parameters or conditions. The curly braces `{ ... }` are required. +The proposal is to support infinite loop via the concise `for { ... }` syntactic construction without parameters or conditions. The curly braces `{ ... }` are required and can not be omitted. It should be used as expression with the result type `Nothing`, but if a loop has a `break` expression, the result type of expression should be `Unit`. @@ -96,7 +101,7 @@ It should be used as expression with the result type `Nothing`, but if a loop ha In Golang infinite loop `for { ... }` is a statement. (see [The Go Language Specification: for clause](https://go.dev/ref/spec#ForClause)). On the other hand, in Rust `loop { ... }` is an expression. (see [The Rust Reference: infinite loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops)) -A loop expression without an associated break expression has type `!`([Never type](https://doc.rust-lang.org/reference/types/never.html)) +A loop expression without an associated break expression has type `!` ([Never type](https://doc.rust-lang.org/reference/types/never.html)). Meanwhile, `break` expressions of Rust can have a [loop value](https://doc.rust-lang.org/reference/expressions/loop-expr.html#break-and-loop-values) only for infinite loops, e.g. ```rust @@ -133,11 +138,16 @@ val x = run { } } ``` -Moreover, the porpose infinite loop `for { ... }` with `Nothing`/`Unit` types is backwards compatible with old code. +Moreover, the proposed infinite loop `for { ... }` with `Nothing`/`Unit` types is backwards compatible with old code. ### Should existing loops in Kotlin be expresions? IDE can have an intention to transform loops with `true` condition into proposed infinite loops. It could make sense to make existing loops with a condition (`do-while`, `while`) expressions. +The existing loops would be used as an expression that allows, for example: +```kotlin +var v = getSmth() ?: while( condition ) { } // Error: While is not an expression, and only expressions are allowed here +``` + But it breaks backward compatibility with already written code, for example: ```kotlin @@ -147,12 +157,14 @@ fun foo() = run { } } ``` -After this changing, `foo` will have `Nothing` type instead of `Unit`. Also, it will cause the compiler error `'Nothing' return type needs to be specified explicitly`. +After this change, `foo` would have `Nothing` type instead of `Unit`. Also, it would cause the compiler error `'Nothing' return type needs to be specified explicitly`. +So the loops with a condition should be left statements. + ### Functions with expression body - `return` is also frequently used to exit from an infinite loop, but `return`s are not allowed for functions with expression body in Kotlin, e.g. + `return` is frequently used to exit from an infinite loop as well, but `return`s are not allowed for functions with expression body in Kotlin, e.g. ```kotlin fun test() = while (true) { @@ -170,26 +182,26 @@ Infinite loops can be implemented via a function (like `repeat` in StdLib), but - Existed local `return` can become misleading with a labelled `break`/`continue` expression. A user can expect a local `return` should exit a loop-like function at all. It might need to change the behavior of local `return` for loop-like function. -```kotlin - (0..9).forEach { - if (it == 0) return@forEach // currently, it has the same behavior as `continue@forEach` - println(it) - } -``` + ```kotlin + (0..9).forEach { + if (it == 0) return@forEach // currently, it has the same behavior as `continue@forEach` + println(it) + } + ``` - For a function of infinite loop inside a loop (`for`, `while`), it might require extra labels that make code verbose. See [KEEP-326](https://github.com/Kotlin/KEEP/issues/326): an unlabeled `break`/`continue` goes to the innermost enclosing block that is clearly marked with `for`, `do`, or `while` hard keywords: -``` -for (elem in 1..10) { - (1..10).someInfiniteLoopFunction { - println(it) - if (it == 5) break@someInfiniteLoopFunction + ```kotlin + for (elem in 1..10) { + (1..10).someInfiniteLoopFunction { + println(it) + if (it == 5) break@someInfiniteLoopFunction + } } -} -``` + ``` - The implementation of the feature is more complicated than infinite loops. @@ -203,7 +215,7 @@ Main candidates of the keyword: The Golang (Go) uses it. Probably `for` is shortened to `forever`. - `do { ... }` But `do {} while(...) {}` can be ambiguous so for a user. A user should check the end of a loop to differ `do-while` and `do` loops. - `loop { ... }` - This keyword is used Rust for infinite loops. + This keyword is used in Rust for infinite loops. - `repeat { ... }` @@ -233,6 +245,6 @@ for { } ``` -Also, factoring this form both away from and back to having a condition is natural. +Additionally, factoring this form both away from and back to having a condition is natural. From 414b92c9892cc7447da8d43d7191c06622caa0de Mon Sep 17 00:00:00 2001 From: vmishenev Date: Wed, 15 Nov 2023 19:42:08 +0200 Subject: [PATCH 5/6] Replace readLine with readlnOrNull --- proposals/infinite-loop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/infinite-loop.md b/proposals/infinite-loop.md index e0ca2b6dd..24e90b670 100644 --- a/proposals/infinite-loop.md +++ b/proposals/infinite-loop.md @@ -27,7 +27,7 @@ The idiomatic approach to reading all the lines from the input stream until it i ```kotlin while(true) { - val line = input.readLine() ?: break + val line = input.readlnOrNull() ?: break // process line } From 8f606b7b4b390fcc98e7b3c0ccdd9aecc9fab3e7 Mon Sep 17 00:00:00 2001 From: vmishenev Date: Mon, 27 Nov 2023 13:12:00 +0200 Subject: [PATCH 6/6] Add a link to discussion --- proposals/infinite-loop.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/infinite-loop.md b/proposals/infinite-loop.md index 24e90b670..a518dcab9 100644 --- a/proposals/infinite-loop.md +++ b/proposals/infinite-loop.md @@ -6,6 +6,7 @@ * **Status**: In Progress * **Prototype**: In Progress (inferring `Nothing` is implemented) * **Issue**: [KT-27970](https://youtrack.jetbrains.com/issue/KT-27970/Support-an-infinite-for-loop) +* **Discussion**: [#364](https://github.com/Kotlin/KEEP/issues/364) ## Summary