Let's say I have a variable x
that is of type Option[Double]
. I want to get that double out of the variable, so I would suspect that x.getOrElse(None)
would be the solution.
The type signature of getOrElse
is as follows:
def getOrElse[B >: A](default: => B): B
None
is simply an Option[Nothing]
. If I write the following:
def mean(xs: Seq[Double]): Option[Double] =
if (xs.isEmpty) None
else Some(xs.sum / xs.length)
val avg = mean(xs) getOrElse(None) // compiles
val theAvg: Double = mean(xs) getOrElse(None) // doesn't compile
What is going on here with the types? The REPL tells me(using :t) that avg
is of type Any
. How can this be? Why does the type match in the first place when None
is of type Option[Nothing]
?
What am I not understanding about the types here?
As the type signature says, getOrElse
returns a B
which is a super type of A
(which is the type inside the Option ) .
That makes sense since, if you have a Some(a: A) , then you return that a , which is of type A
, which given it is a subtype of B
, it can be upcasted to B
without any problem. And, if it is a None , then you return the default which is of type B
.
In your case, you are using None for your default, as such the compiler has to figure out what is the least weak upper bound between Double
& Option[Nothing]
, which, as you can see, it is Any
(because Any
is the supertype of every type, and there is not other relation between Double
& Option
in the type hierarchy) .
Now, I believe you are misunderstanding how getOrElse
works, and what is the purpose of None
.
I think this is what you really want.
// Or any other double that makes sense as a default for you.
val avg: double = mean(xs).getOrElse(default = 0.0d)
How can this be?
The inferred type Any
here is a result of the compiler trying to find a common ancestor of Double
and Option[Nothing]
. The first common ancestor for both these types is Any
, and that is why the compiler is inferring that.
Why does the type match in the first place when
None
is of typeOption[Nothing]
Great question. Let's inspect the signature for getOrElse
:
final def getOrElse[B >: A](default: => B): B
Let's zoom in on the constraint B >: A
, what does that mean? It means that we can specify any type B
that is a supertype of A
. Why do we want it to be a super type of A
to begin with? Why not A
itself? If the type parameter forced us to use A
here, we'd be constrained to Double
and your example wouldn't compile. To answer that question, we need to look at the definition of Option[A]
:
sealed abstract class Option[+A]
We see that A
has a +
next to it. That plus indicates the presence of covariance. In short, covariance allows us to preserve the "is subtype relation" between types which are themselves defined inside other "container" types, making the following relation hold:
A <: B <=> Option[A] <: Option[B]
Which means if A
is a subtype of B
, we can treat Option[A]
as a subtype of Option[B]
. Why does this matter and what does this have to do with the method signature of getOrElse
? Well, covariance imposes restrictions on type parameters! Covariant type parameters cannot be used as input parameters, or in input parameter position (a position is contravariant if it occurs under an odd number of contravariant type constructors, but the explanation for that is too lengthy so I'll abbreviate), they can only be placed as output parameters in the underlying type containing the type parameter. Thus, the following definition is illegal due to variance laws:
final def getOrElse(default: => A): A
Because A
appears here both in the input type and the output type.
Luis did a good job explaining why you got an Any
for your result type. I just wanted to add that if you wanted to use the Double
for further processing, but still correctly handle a None
, you would typically do that with a map
, to be able to stay inside the Option
until you actually have a useful value to replace None
with, like:
val message: Option[String] = avg map {x => s"The average is $x."}
println(message getOrElse "Can't take average of empty sequence.")
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.