I've seen a number of assertions that Scala Enumeration is not type safe. How is it not type safe? It seems type safe in the obvious way in that you can not pass a value of one Enumeration to a different Enumeration.
What are the pitfalls or things to avoid with Enumeration?
While Scala is has a compiler that can help you catch errors, and many call it "type-safe", there is in fact a whole range of ways you can write Scala that provide greater- or lesser- amounts of safety.
An enumeration refers to a group of named constants. Scala provides an abstract class called Enumeration to create and retrieve enumerations.
It's semi-safe. That it is type safe is a compiler fiction, so it's easy to break. For example,
trait Parent class Boy extends Parent { override def toString = "boy" } class Girl extends Parent { override def toString = "girl" } def f(g: Girl) = g.toString scala> f((new Boy).asInstanceOf[Girl]) java.lang.ClassCastException: Boy cannot be cast to Girl at .<init>(<console>:15) ...
Okay, boys aren't girls.
Now let's try with enumerations:
object Test extends Enumeration { val One, Two = Value } object Probar extends Enumeration { val Uno, Dos = Value } def h(tv: Test.Value) = tv.toString scala> h((Probar.Uno).asInstanceOf[Test.Value]) res0: java.lang.String = Uno
Wait, what?
This fiction leads to other weird behaviors:
def h(pv: Probar.Value) = pv.toString // Add this to the other h in a :paste method h:(pv: Probar.Value)java.lang.String and method h:(tv: Test.Value)java.lang.String at line 9 have same type after erasure: (pv: Enumeration#Value)java.lang.String def h(pv: Probar.Value) = pv.toString
Uh, okay, thanks?
And then since the compiler doesn't really understand Enumeration
as its own construct, it can't help you out in ways you might expect:
scala> def oops(tv: Test.Value) = tv match { case Test.One => "okay" } oops: (tv: Test.Value)java.lang.String // No incomplete match warning? Okay.... scala> oops(Test.Two) scala.MatchError: Two (of class scala.Enumeration$Val) at .oops(<console>:8) ...
So if you use it in relatively limited ways exactly as intended, it provides type safety. But it doesn't have the power and robustness of other patterns, like this one:
// In REPL, :paste the next three lines sealed trait Foo object Bar extends Foo object Baz extends Foo scala> def safe(f: Foo) = f match { case Bar => "okay" } <console>:9: warning: match is not exhaustive! missing combination Baz def safe(f: Foo) = f match { case Bar => "okay" } ^
Thanks, compiler!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With