Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

Language design

More changes: Enum Syntax, Another Deprecation and More

Enum Syntax

Currently the syntax for enums with non-trivial constructors is kind of monstrous:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Message(val str: String) {
ERROR : Message("This is an error")
WARNING : Message("This is a friendly warning")
DEBUG : Message("Ignore this")
}
enum class Message(val str: String) { ERROR : Message("This is an error") WARNING : Message("This is a friendly warning") DEBUG : Message("Ignore this") }
enum class Message(val str: String) {
    ERROR : Message("This is an error")
    WARNING : Message("This is a friendly warning")
    DEBUG : Message("Ignore this")
}

We would really like to make it nicer, e.g. something like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Message(val str: String) {
ERROR("This is an error")
WARNING("This is a friendly warning")
DEBUG("Ignore this")
}
enum class Message(val str: String) { ERROR("This is an error") WARNING("This is a friendly warning") DEBUG("Ignore this") }
enum class Message(val str: String) {
    ERROR("This is an error")
    WARNING("This is a friendly warning")
    DEBUG("Ignore this")
}

Now, there are some technicalities, namely, enums can have other members:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Example(...) {
A(...)
B(...)
fun foo() { ... }
}
enum class Example(...) { A(...) B(...) fun foo() { ... } }
enum class Example(...) {
    A(...)
    B(...)

    fun foo() { ... }
}

The problem is that A and B can be parsed as annotations on foo(). So, we had some options to consider here.

We are leaning toward putting a separator there between entries and other members:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Example(...) {
A(...)
B(...)
; // this is mandatory
fun foo() { ... }
}
enum class Example(...) { A(...) B(...) ; // this is mandatory fun foo() { ... } }
enum class Example(...) {
    A(...)
    B(...)
    ; // this is mandatory

    fun foo() { ... }
}

The semicolon is only necessary when some members follow it.

Other options include requiring escaping on all annotations on members (and, possibly, modifiers too):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Example(...) {
A(...)
B(...)
@inject fun foo() { ... }
}
enum class Example(...) { A(...) B(...) @inject fun foo() { ... } }
enum class Example(...) {
    A(...)
    B(...)

    @inject fun foo() { ... }
}

This would be a little inconsistent with normal classes, traits etc.

Or we could prefix enum entries with a (soft-)keyword:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Example {
entry A
entry B
}
enum class Example { entry A entry B }
enum class Example {
    entry A
    entry B
}

This looks too verbose.

Another question here is how do we annotate enum entries themselves. Requiring escaping looks reasonable here:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
enum class Example {
@Ann1 A
@Ann2(...) B
}
enum class Example { @Ann1 A @Ann2(...) B }
enum class Example {
    @Ann1 A
    @Ann2(...) B    
}

Other options include

  • requiring a comma between enum literals (like in Java)
  • requiring a newline between enum literals and allowing unescaped annotations on the same line

We have not decided which way to go on this one yet.

Prohibiting break/continue in when-expressions

We are planning to implement continue in when-expressions as a jump to the next when-entry. It is not implemented yet, but we want your code to stay unchanged when we add it, so for the time being, we prohibit using continue in when without a label that points to a loop:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
loop@
for (...) {
when (...) {
... -> if (...) continue@loop else ... // OK
... -> if (...) continue // ERROR
}
}
loop@ for (...) { when (...) { ... -> if (...) continue@loop else ... // OK ... -> if (...) continue // ERROR } }
loop@
for (...) {
    when (...) {
        ... -> if (...) continue@loop else ... // OK
        ... -> if (...) continue // ERROR
    }
}

We also prohibit unlabeled break inside when. While it is not decided whether we want to allow it ever, it seems like a better design to keep break and continue symmetric.

NOTE: A simple interpretation of break inside when is “stop matching and jump outside” (as in switch in Java and C), but our when often returns a value, and that would be unknown if we break out of it.

Rename traits to interfaces

Well, we picked the names long time ago, and now what we call “trait” in Kotlin is not so much of a trait, and is exactly like Java’s interface nowadays, so we want to deprecate the usage of the trait keyword, and introduce interface in M12.

Feedback request: Let the flame begin :)

image description