【发布时间】:2011-03-06 21:09:06
【问题描述】:
我最近刚刚遇到“弱一致性”一词(在 Stack Overflow 用户 retronym 对 How to set up implicit conversion to allow arithmetic between numeric types? 的回答中)。
这是什么?
【问题讨论】:
我最近刚刚遇到“弱一致性”一词(在 Stack Overflow 用户 retronym 对 How to set up implicit conversion to allow arithmetic between numeric types? 的回答中)。
这是什么?
【问题讨论】:
根据 Scala 语言规范 2.8:
http://www.scala-lang.org/archives/downloads/distrib/files/nightly/pdfs/ScalaReference.pdf
3.5.3 弱一致性
在某些情况下,Scala 使用更一般的一致性关系。 A型S弱
符合类型 T ,写作 S <:w t s>
字节 <:w>
字节 <:w>
短 <:w int>
整数 <:w>
长 <:w>
浮点 <:w>
弱最小上界是弱一致性的最小上界。
【讨论】:
Char 也符合Int。
为了完成Sandor's answer,2.8 中的新功能仍在烘焙(和修复)中。
在this thread 中,Esser 发现了一个令人讨厌的副作用:
scala> val a= 10
a: Int = 10
scala> val b= 3
b: Int = 3
scala> if (b!=0) a/b else Double.NaN
res0: Double = 3.0
scala> def div1(a: Int, b: Int) = if (b!=0) a/b else Double.NaN
div1: (a: Int,b: Int)Double
scala> def div2(a: Int, b: Int): Double = if (b!=0) a/b else Double.NaN
div2: (a: Int,b: Int)Double
scala> div1(10,3)
res1: Double = 3.0
scala> div2(10,3)
res2: Double = 3.3333333333333335
看起来很有趣,因为隐式找到的结果类型是
Double,结果是3.0。
如果明确给出 Double,则结果为 3.33...
在this thread,Martin Odersky 补充道(6 月 21 日):
您在重载决议中发现了弱一致性规则的严重意外副作用。
问题是重载方法的参数需要弱一致性,而结果类型需要强一致性.如果结果类型是浮点数,这有利于
Int上的Float => Float加法方法而不是Int => Int方法。
我试图在更改为弱一致性时保持保守,因为我只在看起来绝对必要的情况下才需要弱一致性。
但现在看来,保守导致了我们正在研究的问题!
又一个 Scala RC 版本 ;)
在this thread by Martin Odersky (June 22d)确认:
所以到目前为止,RC7 将与 RC6 相比有三个变化:
val x: Double = 10/3将给出3.0,而不是3.3333333- 这就是我提到的回归- [...]
- [...]
就是这样。我们现在的首要任务是尽快推出 2.8,同时避免非常糟糕的回归,例如上面的 (1)。
时间线:
- 我们将再等一周以获取有关 RC6 的反馈。
- 我们将在下周初推出 RC7。
如果没有出现进一步的问题,RC7 将在发布 10-14 天后变成 2.8 最终版本。
(我相信是 7 月 12 日左右,但这个猜测是我一个人的猜测;))
【讨论】:
3.5.3 弱一致性 在某些情况下,Scala 使用更通用的 一致性关系。 A型S弱 符合类型 T ,写作 S <:>w T , 如果 S <: t s>
弱最小上界是 关于弱的最小上界 一致性。
这个用在什么地方?一方面,它决定了if 表达式的类型:
条件表达式的类型 是 e2 和 e3 类型的弱最小上界(第 3.5.3 节)
在 Scala 2.7.x 中,这将是 AnyVal 类型,Int 和 Double 的最小上限。在 2.8.x 中,它的类型为 Double。
scala> if (true) 1 else 1d
res0: Double = 1.0
同样:
scala> try { 1 } catch { case _ => 1.0 }
res2: Double = 1.0
scala> (new {}: Any) match { case 1 => 1; case _ => 1.0 }
res6: Double = 1.0
scala> def pf[R](pf: PartialFunction[Any, R]): PartialFunction[Any, R] = pf
pf: [R](pf: PartialFunction[Any,R])PartialFunction[Any,R]
scala> pf { case 1 => 1; case _ => 1d }
res4: PartialFunction[Any,Double] = <function1>
另一个使用它的地方是类型推断:
scala> def foo[A](a1: A, a2: A): A = a1
foo: [A](a1: A,a2: A)A
scala> foo(1, 1d)
res8: Double = 1.0
scala> def foos[A](as: A*): A = as.head
foos: [A](as: A*)A
scala> foos(1, 1d)
res9: Double = 1.0
也适用于简单的数字扩展:
数值加宽。如果 e 有一个原语 弱符合的数字类型 (§3.5.3)到预期的类型,它是 使用一个扩大到预期的类型 的 6.26 隐式转换 97 种数值转换方法 toShort、toChar、 toInt, toLong, toFloat, toDouble §12.2.1 中定义。预期类型是 原始数字类型 Byte、Short 或 Char,表达式 e 是 适合范围内的整数文字 该类型的,它被转换为 该类型中的相同文字。
scala> 1: Double
res10: Double = 1.0
更新
正如 Daniel 所指出的,规范对于哪些类型具有弱一致性是错误的。让我们问问编译器本身:
scala> :power
** Power User mode enabled - BEEP BOOP **
** scala.tools.nsc._ has been imported **
** New vals! Try repl, global, power **
** New cmds! :help to discover them **
** New defs! Type power.<tab> to reveal **
scala> settings.maxPrintString = 10000
scala> import global.definitions._
import global.definitions._
scala> (for{c1 <- ScalaValueClasses;
c2 <- ScalaValueClasses
isNSC = isNumericSubClass(c1, c2)
if isNSC
} yield ("isNumericSubClass (%s, %s) = %b" format (c1, c2, isNSC))).mkString("\n")
res5: String =
isNumericSubClass (class Byte, class Byte) = true
isNumericSubClass (class Byte, class Short) = true
isNumericSubClass (class Byte, class Int) = true
isNumericSubClass (class Byte, class Long) = true
isNumericSubClass (class Byte, class Float) = true
isNumericSubClass (class Byte, class Double) = true
isNumericSubClass (class Short, class Short) = true
isNumericSubClass (class Short, class Int) = true
isNumericSubClass (class Short, class Long) = true
isNumericSubClass (class Short, class Float) = true
isNumericSubClass (class Short, class Double) = true
isNumericSubClass (class Int, class Int) = true
isNumericSubClass (class Int, class Long) = true
isNumericSubClass (class Int, class Float) = true
isNumericSubClass (class Int, class Double) = true
isNumericSubClass (class Long, class Long) = true
isNumericSubClass (class Long, class Float) = true
isNumericSubClass (class Long, class Double) = true
isNumericSubClass (class Char, class Int) = true
isNumericSubClass (class Char, class Long) = true
isNumericSubClass (class Char, class Char) = true
isNumericSubClass (class Char, class Float) = true
isNumericSubClass (class Char, class Double) = true
isNumericSubClass (class Float, class Float) = true
isNumericSubClass (class Float, class Double) = true
isNumericSubClass (class Double, class Double) = true
【讨论】: