Why does Scala 3's `enum` allows `var` for attributes

I just ran the following Scala 3 code in Scastie (which blows up Scastie if 3.0.2 is selected, but works if 3.1.0-RC1 is selected) which uses a var (not a val) to define the rgb attribute.

Is this supposed to compile? IOW, why aren’t all the attributes to an enum forced to be a val?

enum Color(var rgb: Int):
  case Red   extends Color(0xFF0000)
  case Green extends Color(0x00FF00)
  case Blue  extends Color(0x0000FF)

val c = Color.Red
println(c)
println(c.rgb)
println("----------")
c.rgb = 0x00FF00
println(c)
println(c.rgb)

This is similar to the discussion if case classes should allow var-parameters and other var-members or not. I guess it is reasonable to argue by regularity that, as the enum type expands to an abstract class, and you actually need to write modifiers like val or var or private val etc for enum params, then any such relevant modifier should be allowed.

If one argues otherwise, that it should be a deliberate restriction to only allow val-parameters and val-members in enums, then perhaps you shouldn’t even have to write val at all as with case-classes. But still enums would then be irregular wrt case classes, as the latter do allow mutable parameters and other mutable members.

Note that a parameter is a “special case” of a member that is initialized during construction, so creating mutable enums would still be possible e.g. by including var-members (as with case classes etc), (assuming no hypothetical restriction on var-members in enums, not even private var):

scala> enum Mutant:
     |   var x: Int = 0
     |   case Red, Green
     | 
// defined class Mutant

scala> val x = Mutant.Red
val x: Mutant = Red

scala> x.x = 42                                                                                                                             

scala> x.x                                                                                                                                  
val res0: Int = 42

More general, this is a choice of pragmatism or purism and Scala tends to go with the former. I think the behaviour of 3.1.0-RC1 makes sense given how enums are expanded:
https://docs.scala-lang.org/scala3/reference/enums/desugarEnums.html

1 Like

Very likely, as Scala 3 continues to gain popularity, we’ll start to see linters that can flag such things. At least, they existed in Scala 2 (for instance, WartRemover). Very likely these would be in the form of scalafix lints, but currently, Scala 3 still lacks some facilities to make it easy for scalafix to do what it needs.

1 Like