An excellent source for how desugaring of enums works is the original proposal at Issue #1970.
The relevant section for your question on that page is titled "Desugarings". Let's take a look at your Option definition.
enum Option[+T]:
case Some(x: T)
case None
Some is a parameterized case under an enum with type parameters, so Rule #4 applies
If E is an enum class with type parameters Ts, then a case in its companion object without an extends clause
case C <params> <body>
...
For the case where C does not have type parameters, assume E's type parameters are
V1 T1 > L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0)
where each of the variances Vi is either '+' or '-'. Then the case expands to
case C <params> extends E[B1, ..., Bn] <body>
So your Some case gets an extends clause for Option[T], as you'd expect. Then Rule #5 gets us our actual case class.
On the other hand, your None, by the same token, uses no type parameters, so the result is based on variance.
- If
T is invariant, we have to specify an extends clause explicitly
- If
T is covariant, we get Nothing
- If
T is contravariant, we get Any
Your T is covariant, so we extend Option[Nothing], which remember is a subtype of Option[S] for any S. Hence, your assignment (and remember, in Scala, we use val / var to declare variables, not just the type name)
val x: Option[String] = None;
None is a value of type None.type, which extends Option[Nothing]. Option[Nothing] is a subtype of Option[String] since Nothing is a subtype of String. So the assignment succeeds.
This is the same reason Nil (the empty list) extends List[Nothing]. I can construct a Nil which has a concrete type (a subtype of List[Nothing]), and then later I can come along and prepend whatever I want to it. The resulting list is no longer a List[Nothing]; 1 +: Nil is a List[Int] and "a" +: Nil is a List[String], but the point is, that Nil can come from anywhere in my program and we don't have to decide what type we want it to be up-front. We can't (safely) use covariance on a mutable data type, so this all only works because List and Option are immutable. A mutable collection like ArrayBuffer is invariant in its type parameter. (Note: Java's built-in arrays are covariant, and that's unsound and causes all kinds of problems)