Dotty: Enum implementation overrides custom `toString`

Created on 14 Sep 2019  路  14Comments  路  Source: lampepfl/dotty

When overriding toStringin an enum, simple cases end up using their name instead of this new implementation.

minimized code

enum E:
  case A
  override def toString: String = "overriden"

println(E.A)

expectation

overriden

result

A

Note that adding an argument to that enum case will return the right result again:

enum E:
  case A(arg: Unit)
  override def toString: String = "overriden"

println(E.A)

This prints overriden as expected.

This has something to do with the desugaring of enums, and the generation of a default toString method.

enums bug

All 14 comments

This might be working as intended (which doesn't mean it isn't an issue...).

You example is overriding the toString method from trait E. As explain in the enum spec, parametrized enum desugar to case classes while parameterless enums desugar to their own custom thing. Case classes get a default toString method unless they already have/inherit a toString method other than the one from Object. On the other hand, the "custom thing" that parameterless enums desugar to unconditionally override toString.

Yeah it does make sense considering how the implementation is.
I do think it does make it a somewhat leaky abstraction, because I need to reason about how the implementation of enum happens to work in order to understand things like this.

I do think it does make it a somewhat leaky abstraction, because I need to reason about how the implementation of enum happens to work in order to understand things like this.

Totally agree, I think having enum specified as a desugaring step is not really ideal...

Ideal or not, that's how enums are currently specified. So far there is a better alternative.

Could we fix this issue by desugaring parameter-less enums to case objects instead of vals?

I don't think it is about vals vs case objects. Translation specifies that a case of an enum is an instance of an anonymous class extending the parent enum and overriding its toString method. The same can be done via an object but to change the semantics we need to change the spec of what an enum case is translated to.

Yes. vals and objects behave the same here. It makes no difference wether I write case A (which is implemented by a val) or case A extends E (which is implemented by an object). But for consistency it would be good if toString was not affected by whether a case is parameterized or not. Right now we get in

enum E {
  case A
  case B()
  override def toString: String = "overriden"

println(E.A)    // --> A
println(E.B())  // --> overridden

It would be good if we could change everything to overridden. This means we need a separate method to retrieve the name of an enum value; toString will not work in this role anymore.

So, to fix this, we need a redesign of (this aspect of) enums, and an implementation.

We should reconsider adding name methods to Scala enums

@anatoliykmetyuk Indeed. And make the toString final and return the value of name?

my current idea is since scala.Enum extends Product we can just use productPrefix instead of adding name

my current idea is since scala.Enum extends Product we can just use productPrefix instead of adding name

I don't think that's a good idea. While reasonable from DRY and engineering perspective, it adds a mental load on the programmer's mind. Now, to properly use enums, we need to keep in mind that they are products. For new programmers, this can be an extra learning curve when learning enums.

@anatoliykmetyuk Thanks for your advice, I've said in this comment why at least the name method is difficult to add:
https://github.com/lampepfl/dotty/pull/9549#issuecomment-674000889

as per https://github.com/lampepfl/dotty/pull/9549#issuecomment-674033755 I can add an enumLabel method which should hopefully be simple to remember and not clash with any fields in class cases

I like enumLabel, I think that's a good name. Thanks for implementing it!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m-sp picture m-sp  路  3Comments

NightMachinary picture NightMachinary  路  3Comments

DarkDimius picture DarkDimius  路  3Comments

odersky picture odersky  路  3Comments

deusaquilus picture deusaquilus  路  3Comments