Match types do not treat 0.0d and -0.0d as equivalent constants
Compiler version
Scala 3.7.0 (Confirmed with both sbt and Scala CLI)
Minimized code
type DoubleToString[D <: Double] <: String = D match
case 0.0 => "0.0"
case _ => "_"
@main def main(): Unit =
summon[DoubleToString[0.0] =:= "0.0"]
summon[DoubleToString[-0.0] =:= "0.0"]
summon[DoubleToString[-0.0] =:= "_"]
Compilation output
[error] -- [E172] Type Error: src/main/scala/Main.scala:7:40
[error] 7 | summon[DoubleToString[-0.0] =:= "0.0"]
[error] | ^
[error] | Cannot prove that DoubleToString[(-0.0d : Double)] =:= ("0.0" : String).
[error] |
[error] | Note: a match type could not be fully reduced:
[error] | trying to reduce DoubleToString[(-0.0d : Double)]
[error] | failed since selector (-0.0d : Double)
[error] | is uninhabited (there are no values of that type).
[error] -- [E172] Type Error: src/main/scala/Main.scala:8:38
[error] 8 | summon[DoubleToString[-0.0] =:= "_"]
[error] | ^
[error] | Cannot prove that DoubleToString[(-0.0d : Double)] =:= ("_" : String).
[error] | Note: a match type could not be fully reduced:
[error] | trying to reduce DoubleToString[(-0.0d : Double)]
[error] | failed since selector (-0.0d : Double)
[error] | is uninhabited (there are no values of that type).
Problem description
In Scala 3.7.0, match types cannot distinguish or unify 0.0 and -0.0, even though:
0.0d == -0.0dholds at runtime- Using
=:=on the type level compiles successfully:summon[0.0 =:= -0.0]works
However, in match types, writing case 0.0 => causes -0.0 to be rejected as an uninhabited selector.
Switching to case -0.0 => then rejects 0.0.
This suggests that match types are not treating 0.0d and -0.0d as equivalent constant values, which is inconsistent with how =:= treats them.
Expected behavior
Match types on Double constants should not distinguish 0.0 and -0.0, just as =:= does not.
- Using
=:=on the type level compiles successfully:summon[0.0 =:= -0.0]works
Huh. To me, that is the real bug. +0.0 and -0.0 definitely are not the same value. They should not have the same literal type. For example, literal types can cause constant-folding. You don't want to constant-fold -0.0 into +0.0, since it can change the result of computations.
+0.0and-0.0definitely are not the same value.
If it is a value (not a type), the comparison is a match - this appears to be a JVM specification.
println(0.0 == -0.0) // => true
https://docs.oracle.com/javase/specs/jvms/se24/html/jvms-2.html#:~:text=Positive%20zero%20and%20negative%20zero%20compare%20equal
Since 0.0 and -0.0 as values are indistinguishable, I think it would be better if literal types did not distinguish between 0.0 and -0.0 either. My expectation is that comparisons and operations on literal types work the same as on values.
+0.0 and -0.0 are definitely not indistinguishable. You can tell them apart with 1.0/x, for example. The fact that they compare == does not mean they are the same value or that are indistinguishable.
Conversely, NaN == NaN is false, but NaN is the same value as NaN.
In general, == cannot be trusted to answer the question "are the operands the same value/indistinguishable?".
As for 0, since values do not distinguish between positive and negative, I thought it would be better to not distinguish between positive and negative in literal types as well, so that operations could be related to values and types. In fact, positive and negative are not distinguished outside of match types.
However, does this mean that it is better to distinguish between 0.0 and -0.0 for literal types?
(Personally, regardless of the conclusion, I would like the behavior of match types to be the same as others. In other words, if we go with the current situation, I would not like the distinction between positive and negative. If we go with your proposal, I would like the distinction to be made in behavior other than match types, and I would like to be able to put positive and negative zeros in case clauses.)
However, does this mean that it is better to distinguish between
0.0and-0.0for literal types?
Yes, that's what I meant in my first comment.