|
| 1 | +# Underscore syntax for unused local variables |
| 2 | + |
| 3 | +* **Type**: Design proposal |
| 4 | +* **Authors**: Leonid Startsev, Mikhail Zarechenskiy |
| 5 | +* **Contributors**: Alejandro Serrano Mena, Denis Zharkov, Marat Akhin, Nikita Bobko, Pavel Kunyavskiy, Vsevolod Tolstopyatov |
| 6 | +* **Status**: Experimental in Kotlin 2.2 |
| 7 | +* **Discussion**: [KEEP-412](https://github.com/Kotlin/KEEP/issues/412) |
| 8 | + |
| 9 | +## Synopsis |
| 10 | + |
| 11 | +The gist of this proposal is allowing `_` (one underscore) as a local variable name. |
| 12 | +The intention is for the compiler and other static analysis tools to consider that value |
| 13 | +as ignored or unused (depending on the particular scenario): |
| 14 | + |
| 15 | +```kotlin |
| 16 | +fun writeTo(file: File): Boolean { |
| 17 | + val result = runCatching { file.writeText("Hello World!") } |
| 18 | + return result.isSuccess |
| 19 | +} |
| 20 | + |
| 21 | +fun foo(file: File) { |
| 22 | + val _ = writeTo(file) // We are not interested in whether the write operation was successful |
| 23 | +} |
| 24 | +``` |
| 25 | + |
| 26 | +Underscore can be used only in *local variable declaration*: `val _ = ...` and cannot be referenced or resolved otherwise. |
| 27 | + |
| 28 | +## Motivation |
| 29 | + |
| 30 | +1. Explicit expression of intent |
| 31 | + |
| 32 | +When reading and reviewing code you are unfamiliar with, it may be hard to say whether unused function return value is an intention or a subtle bug. |
| 33 | +Explicit syntax would help quickly differentiate between the two. |
| 34 | + |
| 35 | +2. Readiness for static analysis |
| 36 | + |
| 37 | +In the scope of [Unused return value checker](unused-return-value-checker.md) proposal, a checker for more complex unused expressions will be added to the Kotlin compiler. |
| 38 | +This syntax will make its adoption much easier since there would be no need to `@Suppress` checker warnings in cases where the value has to be explicitly ignored. |
| 39 | +Other static analysis tools will also report less false positives with this feature. |
| 40 | + |
| 41 | +3. Uniformity in existing Kotlin features |
| 42 | + |
| 43 | +Kotlin already supports underscores for unused things in several different positions: |
| 44 | + |
| 45 | +* Underscore for unused lambda/anonymous function parameters ([KEEP](underscore-for-unused-parameters.md), [link](https://kotlinlang.org/docs/lambdas.html#underscore-for-unused-variables)). |
| 46 | +* Underscore for unused components in positional-based destructuring ([link](https://kotlinlang.org/docs/destructuring-declarations.html)). |
| 47 | +* Underscore for unused type arguments ([link](https://kotlinlang.org/docs/generics.html#underscore-operator-for-type-arguments)) |
| 48 | +* Underscore for unused exception instances ([link](https://youtrack.jetbrains.com/issue/KT-31567)) |
| 49 | + |
| 50 | +Adding an underscore as a name for unused local variables is a logical extension of this trend. |
| 51 | + |
| 52 | +## Proposed syntax |
| 53 | + |
| 54 | +It is allowed to declare a variable as `val _`. Such a variable is called **unnamed**. |
| 55 | +Unnamed variable declarations should adhere to the following restrictions: |
| 56 | + |
| 57 | +* Only local variables (not class or top-level properties) can be declared unnamed. |
| 58 | +* Only the `val` keyword can be used; `var` is not allowed. |
| 59 | +* You can have multiple unnamed variables in a single scope. |
| 60 | +* Unnamed variables cannot be delegated. |
| 61 | +* Referencing an unnamed variable is not possible. Because of that, it is required to have an initializer. |
| 62 | +* Unnamed variables can have their type specified explicitly. If type is not specified, it is inferred using the same rules as regular variables. |
| 63 | + |
| 64 | +### Discarded alternatives |
| 65 | + |
| 66 | +Besides using `val _ = ...` syntax, a shorter `_ = ...` statement was considered as an alternative. |
| 67 | +However, we've decided not to proceed with it for the following reasons: |
| 68 | + |
| 69 | +1. `val` syntax is better aligned with already existing Kotlin features. |
| 70 | + |
| 71 | +Kotlin already has positional-based destructuring with an option to use an underscore to omit certain components: `val (first, _) = getSomePair()`. Since positional-based destructuring declares new variables, it uses `val` syntax. |
| 72 | + |
| 73 | +On the other hand, Kotlin has the 'underscore for unused lambda parameter' feature. Using underscore without `val` here may imply some connection with the lambda parameter, while in reality, there is no such connection: |
| 74 | + |
| 75 | +```kotlin |
| 76 | +listOf(1, 2, 3).mapIndexed { idx, _ -> |
| 77 | + _ = unusedCall(idx) // Is _ a new unnamed variable or an existing lambda parameter? |
| 78 | +} |
| 79 | +``` |
| 80 | + |
| 81 | +2. `val` syntax implies the creation of a new unnamed variable rather than referencing an existing one. |
| 82 | + |
| 83 | +Normally, without the `val/var` keywords, you can encounter only existing declarations on the left-hand side of an assignment. |
| 84 | +Using `_ = ...` syntax may create an impression that `_` is some existing global variable, while in reality, any expression that uses `_` — such as `println(_)` — would produce 'unresolved reference' compiler error. |
| 85 | + |
| 86 | +## Further extensions |
| 87 | + |
| 88 | +Besides this proposal and already existing underscore usages, one of the most requested places for underscore support is an unused function parameter: |
| 89 | + |
| 90 | +```kotlin |
| 91 | +fun foo(_: String) { |
| 92 | + println("Parameter is unused") |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +However, this use case is explicitly out of the scope of this proposal. |
| 97 | +Its destiny will be decided later. |
0 commit comments