More changes: Enum Syntax, Another Deprecation and More

Posted on by Andrey Breslav

Enum Syntax

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

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:

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:

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:

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):

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:

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:

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:

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 🙂

Comments below can no longer be edited.

59 Responses to More changes: Enum Syntax, Another Deprecation and More

  1. Christian Brüggemann (@cbruegg) says:

    April 7, 2015

    For enums, I’d go with the semicolon + comma solution. I feel like Kotlin is Java on steroids, so there’s no need to forcedly differentiate to it.

    • Salomon BRYS says:

      April 7, 2015

      I agree

  2. m1shk4 says:

    April 7, 2015

    is there a typo in the last code block? label is loo, but continue is called on loop

  3. Alexander Orlov says:

    April 7, 2015

    +1 for using “interface” instead of “trait”, naming same things the same way within the same ecosystem.

    PS: Using U+1F44D inside the “Comment” field, lead to my previous messed up post.

  4. Geobert Quach says:

    April 7, 2015

    Java’s interface can’t have code whereas Kotlin’s trait can

    • Mohammad Shamsi says:

      April 7, 2015

      Well, in Java 8 you can have code in interfaces. default implementation and also static methods.

  5. Mohammad Shamsi says:

    April 7, 2015

    By changing the annotation syntax to java style (@Foo) and dropping the short form for annotations (current implementation), I think, there will be no issue regarding changes in Enum syntax. am I right?

    • Max says:

      April 7, 2015

      I think the same way. Last modifications in Kotlin has been caused by unnecessary (in my opinion) desire to support short form of annotations. This desire makes the language tricky. That’s sad.

      • Jayson says:

        April 7, 2015

        Annotations requiring @ only bothers me for data classes because I like to forget that it is an annotation. Otherwise I don’t see much value in having a short form of annotations. In other areas of the language there is ambiguity because of this… Bring back the @, is fine with me.

        Also, in regards to enums, a comma separator is fine and is one of the biggest mistakes I make with enums in Kotlin every time I create one. For some reason a list of things feels odd without the list separator.

        But, it is nice if we allow optional trailing commas in these type of lists without something following (last item in list). Could be a warning, but makes commenting things in/out in lists easier.

    • Mykhailo says:

      April 9, 2015

      +1

  6. Ladislav Thon says:

    April 7, 2015

    How come trait is not a trait? I admit my knowledge is lacking in this area, but as far as I know, Kotlin’s traits are [stateless] traits. As are Java 8 interfaces, mostly.

    • Andrey Breslav says:

      April 7, 2015

      Well, “mostly” is the key here, for the sake of performance and uniformity, we will likely align semantics of our traits with Java interfaces anyways.

      • Oliver Plohmann says:

        April 7, 2015

        So far traits in Kotlin were intended to support abstract variables and methods that both can also be declared as protected. Is this functionality supposed to be retained in M12 or will this be removed?

        • Andrey Breslav says:

          April 7, 2015

          All functionality (except for required classes) will be retained. Only the keyword changes.

          • Oliver Plohmann says:

            April 7, 2015

            That’s good to hear. Big relief :-).

  7. Даниил Водопьян says:

    April 7, 2015

    The last proposal for the enum syntax looks like class initialisation in C++, which is one of the most HATED syntax features of the language, mostly for its dissimilarity to any other initialisation.

    Enums are relatively rarely used, and having ‘mostrouse’ but uniform syntax is preferable.

    • Andrey Breslav says:

      April 7, 2015

      I wouldn’t say that enums are used rarely enough for this to be ignorable.

      Also, I don’t follow your point about C++. Could you clarify?

      • Даниил Водопьян says:

        April 7, 2015

        Sure. The C++ feature is called ‘initializer list’, used for initializing constant values.

        See the discussion here:
        https://stackoverflow.com/questions/1423696/how-to-initialize-a-const-field-in-constructor/29495307#29495307

        • Andrey Breslav says:

          April 7, 2015

          Do you imply that it is in some way similar to this syntax?

          enum class Message(val str: String) {
              ERROR("This is an error")
              WARNING("This is a friendly warning")
              DEBUG("Ignore this")
          }
          
          • Даниил Водопьян says:

            April 7, 2015

            See the similarity in this way:

            C++ has a special way of assigning values with brackets, while elsewhere in the language it is done with ‘=’.

            You propose a special syntax for initialising instances (objects?) with invoking something on a enum object name, while everywhere else this name is never invoked.

          • Даниил Водопьян says:

            April 7, 2015

            Yes, I think both of these syntax solutions look similar to each other. Of course there is nothing bad in it. This is inconsistency that I am worried about.

            I am saying that the abnormality of the corresponding C++ code is so that I had to google the syntax not once, but many times while learning the language.

            Maybe I am stupid, maybe you don’t want the same for Kotlin 😉

            • Andrey Breslav says:

              April 8, 2015

              Well, this is a very distant analogy, I’m afraid. I assume it does not bother you that values are sometimes assigned to function parameters by enclosing expressions in parentheses (e.g. foo(x))

          • Даниил Водопьян says:

            April 8, 2015

            No jokes on this point, foo(x) means passing a value, not assigning it. With this new syntax ERROR("error") is going be the only place in kotlin where something is invoked without a declaration elsewhere.

            • Andrey Breslav says:

              April 8, 2015

              This “something” is a constructor that is declared in the same enum class.

          • Даниил Водопьян says:

            April 8, 2015

            But this constructor happend to change its name ‘Message’ to ‘ERROR’ somehow!

            • Andrey Breslav says:

              April 8, 2015

              Well, there are other place in Kotlin where something like this happens: e.g. when you use the invoke() convention, you call something whose name differs from the function actually invoked.

              In any case, this discussion is somewhat pointless, because this syntax is exactly what Java has since version 5 and I can’t recall any complaints about it.

          • Даниил Водопьян says:

            April 8, 2015

            Yep, let’s try it, I hope you were right !

            😀
            http://image.slidesharecdn.com/pnwscala2013-131019160049-phpapp01/95/keynote-pnw-scala-2013-11-638.jpg?cb=1382319521

    • Jayson says:

      April 7, 2015

      I use ENUMS heavily, many times instead of string constants so that the list of options is confined. And typically with one constructor parameter and the old syntax is horrible (as pointed out), and something short and clean is preferred.

  8. Jacob Zimmerman says:

    April 7, 2015

    Separate enums with commas. No biggie.
    I wouldn’t use break in any way in a when expression. And even allowing a continue to a label is dangerous, since it breaks out of the expression, which I don’t like. Maybe it can only be used if the expression isn’t being assigned to anything.
    I’m okay with the traits/interface change. Although, can “default” methods in traits be marked as “final”?

    • Andrey Breslav says:

      April 7, 2015

      continue, when implemented, is not supposed to jump outside of the when, but only to the next condition.

    • Andrey Breslav says:

      April 7, 2015

      In Kotlin we will keep allowing marking methods of interfaces final, although in Java class files, this flag will not be present.

      • Jacob Zimmerman says:

        April 7, 2015

        Yeah, I knew you wouldn’t be able to enforce this in Java, but the Kotlin compiler will enforce this? I’m glad.

  9. B7W says:

    April 7, 2015

    I like trait key word. It is short and it tell me that there can be implementation.

    • Jayson says:

      April 7, 2015

      I like trait as well, but as Andrey says, Java 8 Interfaces become very similar including with default methods. Would be nice if Java renamed to trait, but not likely to happen. So unify @annotation requiring @, commas between enum values (end list with ; if you have things following), and rename trait to interface (I’ll be sad, but it makes sense, trait is so much cooler sounding)

      unfortunately we’ll see checkbox language comparisons now saying “Kotlin doesn’t have traits, we do”

  10. farrukh says:

    April 7, 2015

    upto M11 kotlin language was clean and simple now there are so much changes coming i am afraid you people are making language more complected
    +1 for renaming trait to interface

    • Andrey Breslav says:

      April 7, 2015

      Well, we are leaving things out mostly, not adding things, so it hardly complicates the design 🙂

  11. farrukh says:

    April 7, 2015

    loop@ is ugly

    what about this syntax for label loops
    for (…) @loop{
    }

    • Jayson says:

      April 7, 2015

      There is another blog post covering why the @ moves to then end (to disambiguation with annotations)

  12. Philip Lombardi says:

    April 7, 2015

    A comma between Enum entries seems to be the best. I do not think the Java syntax there needs any kind of reinventing.

    I agree with renaming ‘trait’ to ‘interface’ in order to be more consistent with the larger Java ecosystem though I would also reserve the ‘trait’ keyword for later use if Kotlin ever introduces stateful traits like Scala.

  13. Laurent Caillette says:

    April 7, 2015

    Enum syntax

    I like the “entry” keyword. Verbosity is your friend when it adds to legibility; the “entry” keyword typographically differenciates the entries from the rest.

    Please don’t add mandatory commas, Java got it so wrong.

    Prefixing annotations with “@” is OK for me for the same reason.

    Rename traits to interfaces

    +1, the “trait” keyword always looked obscure (Scala-ish?) to me.

  14. Juanjo says:

    April 7, 2015

    Hi,

    First of all, THANK YOU. I truly believe Kotlin is a great language with a great power/easiness trade-off. The only lack is that… well, it is not stable yet!

    My preferences about the post alternatives would be:

    Enums: the comma separated values seems good to me, anyway, is gonna be common to have all of them in the same line. Regarding the semicolon, I though all annotations will be prefixed with ‘@’ from now on (though I’m not sure) if this were the case, will the semicolon still be needed? if that’s an alternative, I would like to keep annotations without ‘@’
    Traits: I’ve seen a lot of opinions about keeping this consistent with Java because Java interfaces now seem like traits… IMHO for me it would be better to keep the language consistent with itself better than with Java. And maybe Java people would like to change ‘interface’ by ‘trait’… but the fact is that they can’t (and at this stage Kotlin can) would this be regreted in the future? It is clear I would prefer ‘trait’ 😀
    Jumps: they maintain their semantics for loops, right? For ‘when’, having a continue seems nice to me, and break doesn’t.

    Thanks!

    • Andrey Breslav says:

      April 8, 2015

      ‘@’ is going to be an optional escape-symbol for annoations

      Jumps’ semantics are preserved for loops.

      • Juanjo says:

        April 9, 2015

        Thanks!

  15. Graham says:

    April 7, 2015

    The idea of annotations working differently depending on the presence or absence of a newline character is not a good one to me. I’d much prefer the comma and semicolon solution. Everyone already understands it, it’s simple and there are no unexpected surprises if you reformat your code.

  16. Elviss says:

    April 8, 2015

    One feature that I use quite often with enums is abstract method overrides for each enum entry. Sample:

    enum class Test {
    T : Test() {
    override fun hello() = "world"
    }

    abstract fun hello(): String
    

    }

    It makes enums span multiple lines and it could make comma separators and/or semicolon feel out-of-place (and almost all other places in Kotlin where”;” is used are optional).
    One alternative idea that I think would look good is optional {} block (mandatory only if there are other members) that can isolate enum entries from other parts of enum code, like:

    enum class Test {
    entries {
    T1
    T2
    }

    fun hello() = "world"
    

    }

  17. Vladimir Krivosheev says:

    April 8, 2015

    -1 to renaming trait to interface. English is not my native language, but for me “interface” it is just a contract. Kotlin trait is an abstract class, but stateless. For example, I can have one contract (EditorService), but if implementation is complex and big, implementors can split it up to several files (ContentAssistService and so on). “trait” is more natural word.

    • Oliver Plohmann says:

      April 9, 2015

      I would also prefer traits not being renamed to interfaces. What really makes a difference to Java besides true closures are extension methods and traits. Those things are to me the big points. From what I know traits originate from Self. Also Ceylon, Scala, Rust, Squeak Smalltalk and now also Groovy have them (although in Groovy they are just JDK8 interfaces with defender methods). People could think that Kotlin doesn’t have traits because there is no mentioning besides interfaces although the competition all have them.

      A big + for having annotations the same way as in Java. For the time being I use Kotlin when I have to learn new frameworks, tools and things. Knowing it would be just the same way in Java when I do the same thing at work lets me safely play with Kotlin. I believe this would hold true for others that for the time being want something more modern than Java but don’t have a chance to use Kotlin at work for the time being.

  18. Edoardo Vacchi says:

    April 9, 2015

    I gave my 2 cents on annotation syntax previously on the forums

    In short, in my opinion brackets may be optional when the annotation has no arguments, and mandatory in every other case. This would be also a good occasion to change the annotation syntax, dropping parentheses; suppose:

    annotation class foo(bar: String = "baz", qux: String = "quux")

    then, to annotate a fun:

    [foo bar="baz" qux="quux"] fun myfun() = ...

    but allowing:

    foo fun myfun() = ...

    this should remove any ambiguity in enums (and anywhere else).

    As for interface v. trait. I see your point in switching back to interface. On the other hand Java 8’s default methods should be used as a means to evolve APIs (at least, this is what JDK devs say). I feel like “traits” should be thought of a first-class inheritance mechanism, rather than a fallback. But I also see that the overlap may be confusing.

    • Andrey Breslav says:

      April 9, 2015

      Unfortunately, your proposal will not remove the ambiguity with enums, because enum literal often have no () next to them. Nor does it help with local functions, because there are infix calls: list map fun foo() {}

      • Max says:

        April 9, 2015

        That is why better to leave only one explicit syntax for annotations.

    • Edoardo Vacchi says:

      April 9, 2015

      On the other hand Java 8′s default methods should be used as a means to evolve APIs (at least, this is what JDK devs say).

      cf:
      http://zeroturnaround.com/rebellabs/how-your-addiction-to-java-8-default-methods-may-make-pandas-sad-and-your-teammates-angry/
      http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008435.html

      • Oliver Plohmann says:

        April 15, 2015

        I don’t really see the point being made in the article from Zeroturnaround with a default method that is left empty as a stub for now. The same problems arises with methods in abstract classes that are supposed to serve as a default implementation for subclasses. I have never heard anyone saying that this were a general problem and not just a DPE.

  19. Anton Danshin says:

    April 11, 2015

    Why do you need @ for annotations when you already have brackets: [ … ]? For enums you can require to use square annotations with these brackets.
    Plus, @ is used in return@, and this@

    I would also vote for a semicolon at the end of enum-entries declaration.

  20. Steven Gertiser says:

    April 14, 2015

    I think that the word trait is more apt if traits go beyond the functionality of a standard Java interface in the future. Some examples of that would be traits with state as in Scala for mixed-in inheritance, or (even better!) a trait with a defined default implementation.

    I would hope for feature enhancement in this direction, so trait as a keyword should be reserved in any event.

  21. Scott says:

    April 16, 2015

    I find the commas in Java enums to be annoying. I think they should be separated with lines. I understand the sentiment to go with what’s familiar, but why force writing something when you don’t have to?

  22. Isak says:

    May 5, 2015

    I think the enum constructors are just fine as they are. Monstrous? No. Adding commas or mandatory semicolons is certainly worse. The constructors, as they are just now, are consistent with the rest of kotlin.