将-=与不兼容的类型(Double和DenseVector)一起使用会导致奇怪的scala编译器错误

将-=与不兼容的类型(Double和DenseVector)一起使用会导致奇怪的scala编译器错误,scala,compiler-errors,scala-breeze,Scala,Compiler Errors,Scala Breeze,我是scala新手,在scala编译器遇到一个最初完全没有信息的NullPointerException后,我现在能够缩小问题范围并消除其原因,但我仍然不完全理解编译器的行为 环境: IntelliJ IDEA 2020.2.3、JDK 1.8.0_271、Scala 2.13.3、SBT 1.3.13、Breeze 1.1 编辑:使用sbt 1.4.2命令行中的sbt compile在没有IntelliJ的情况下进行验证,编译器行为完全相同 我目前观察到的: MWE: 在双值和行中的Dense

我是scala新手,在scala编译器遇到一个最初完全没有信息的NullPointerException后,我现在能够缩小问题范围并消除其原因,但我仍然不完全理解编译器的行为

环境:

IntelliJ IDEA 2020.2.3、JDK 1.8.0_271、Scala 2.13.3、SBT 1.3.13、Breeze 1.1

编辑:使用sbt 1.4.2命令行中的
sbt compile
在没有IntelliJ的情况下进行验证,编译器行为完全相同

我目前观察到的:

MWE:

在双
值和行中的DenseVector
v
上错误地使用运算符
+=

    value += v
value += 0.5 * v.t * m * v
在IDE中及时、正确地以红色突出显示这一点。 试图编译它,我得到了有用的信息

value += is not a member of Double
  Expression does not convert to assignment because:
    overloaded method + with alternatives:
      (x: Double)Double <and>
      (x: Float)Double <and>
      (x: Long)Double <and>
      (x: Int)Double <and>
      (x: Char)Double <and>
      (x: Short)Double <and>
      (x: Byte)Double
     cannot be applied to (breeze.linalg.DenseVector[Double])
    expansion: value = value.<$plus: error>(v)
    value += v
第一个问题:为什么这两个案例表现出不同的行为? 我在scala中对这些操作符的处理做了一个简短的了解(参见示例),在查看了scala文档(尤其是)之后,我没有发现任何理由对
+=
-=
进行不同的处理

回到最初的代码,行为上的差异以另一种方式表现出来:

在行中使用
+=

    value += v
value += 0.5 * v.t * m * v
结果与上面描述的更简单的情况类似,因为右侧的操作生成DenseVector类型的结果(下面将进一步介绍),我们尝试将其添加到Double中。IDE中的运算符标记为红色,并显示相同的编译器错误

但是,使用
-=
显示的编译器错误与更简单的情况完全不同:

scalac: Error while emitting MWE.scala
assertion failed: 
  Bad superClass for class Double: <none>
     while compiling: C:\Users\bt306644\typeBugMWE\src\main\scala\MWE.scala
        during phase: jvm
     library version: version 2.13.3
    compiler version: version 2.13.3
  reconstructed args: -classpath C:\Program Files\Java\jdk1.8.0_271\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_271\jre\lib\rt.jar;C:\Users\bt306644\typeBugMWE\target\scala-2.13\classes;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\com\chuusai\shapeless_2.13\2.3.3\shapeless_2.13-2.3.3.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\com\github\fommil\netlib\core\1.1.2\core-1.1.2.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\com\github\wendykierp\JTransforms\3.1\JTransforms-3.1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\net\sf\opencsv\opencsv\2.3\opencsv-2.3.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\net\sourceforge\f2j\arpack_combined_all\0.1\arpack_combined_all-0.1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\apache\commons\commons-math3\3.5\commons-math3-3.5.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\scala-lang\modules\scala-collection-compat_2.13\2.1.1\scala-collection-compat_2.13-2.1.1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\scala-lang\scala-library\2.13.3\scala-library-2.13.3.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\scala-lang\scala-reflect\2.13.3\scala-reflect-2.13.3.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\scalanlp\breeze-macros_2.13\1.1\breeze-macros_2.13-1.1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\scalanlp\breeze_2.13\1.1\breeze_2.13-1.1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\slf4j\slf4j-api\1.7.5\slf4j-api-1.7.5.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\algebra_2.13\2.0.0-M2\algebra_2.13-2.0.0-M2.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\cats-kernel_2.13\2.0.0-M4\cats-kernel_2.13-2.0.0-M4.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\machinist_2.13\0.6.8\machinist_2.13-0.6.8.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\spire-macros_2.13\0.17.0-M1\spire-macros_2.13-0.17.0-M1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\spire-platform_2.13\0.17.0-M1\spire-platform_2.13-0.17.0-M1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\spire-util_2.13\0.17.0-M1\spire-util_2.13-0.17.0-M1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\org\typelevel\spire_2.13\0.17.0-M1\spire_2.13-0.17.0-M1.jar;C:\Users\bt306644\AppData\Local\Coursier\cache\v1\https\repo1.maven.org\maven2\pl\edu\icm\JLargeArrays\1.5\JLargeArrays-1.5.jar

  last tree to typer: Literal(Constant(0.5))
       tree position: line 6 of C:\Users\bt306644\typeBugMWE\src\main\scala\MWE.scala
            tree tpe: Double(0.5)
              symbol: null
           call site: constructor MWE in object MWE in package <empty>

== Source file context for tree position ==

     3   def test(v: DenseVector[Double], m: DenseMatrix[Double]): Unit = {
     4     var value = 0.5
     5 //    value += v
     6     value -= 0.5 * v.t * m * v
     7  }
     8 }
     9 
由于我对导致NPE的原因一无所知,所以需要一些时间来缩小范围。NPE似乎是scala编译器中的一个bug,已在2.13中修复,请参见示例(如果我在假设连接时出错,请纠正我)

实际上,导致这一切出现的原因仍然有些奇怪(不,我没想到能从双xD中减去DenseVectors)。 初始代码中的操作稍微复杂一些,但基本上可以归结为MWE中的这一行:

value -= 0.5 * v.t * m * v
这实际上根本不应该产生一个DenseVector

撇开0.5不谈,我们乘以一个转置向量、一个方阵和一个向量,所有向量都具有相同的维数。此操作将生成标量,而不是向量

然而,0.5在代码中弄乱了这一点(至少在类型方面)-在转置的DenseVector上乘以标量(
0.5*v.t
)并不会产生预期的转置DenseVector,结果是DenseMatrix类型。这将导致整条线路产生不正确的DenseVector类型,而不是Double

附带问题:为什么代码会显示关于类型的这种行为?有人知道这是否是一个合理的选择,或者这可能是一个小虫

因此,我们的原始代码可以通过添加偏执来轻松修复:

value -= 0.5 * (v.t * m * v)
正确的结果是加上两个双打,一切正常


但是问题的原因仍然困扰着我,我期待着收到一些有用的信息:)

第一个问题:这是一个有根据的猜测。Scala对
+
+=
的处理有些奇怪,因为Scala允许您执行
(x:Any)+(y:String)
,并且我看到了不同运算符在错误方面的奇怪差异

第二个问题:这是一个编译器错误,应该得到支持。您可以尝试在scalac中使用
-no specialization
标志运行,看看是否会出现相同的错误。专业化在Breeze中被大量使用,多年来我偶然发现了许多与之相关的编译器错误


附带问题:这是微风中的一个限制/缺陷。Scala将
.5*v.t*m*v
解析为
0.5.*(v.t)。*(m)。*(v)
(即从左到右)
v.t
是一个
Transpose[DenseVector[Double]]
(即行向量),老实说,Transpose的东西并没有我想要的那么充分。从历史上看,我们将行向量表示为矩阵,我怀疑它又回到了这一点,因此它看到了矩阵*矩阵*向量,这当然是一个向量。

我刚刚使用
sbt compile
验证了所有四种情况,编译器的行为完全相同。我可以复制错误,这些错误非常奇怪。我谦虚的假设是,breeze使用一些奇特的
宏来优化它们的操作,这会产生那些奇怪的错误。可悲的是,我不知道还能说些什么,也许值得在breeze上发表一篇文章?谢谢你的意见,我会在有时间的时候尽快发表(很可能是明天),如果相关信息传来,我会更新这个问题。近亲繁殖使用了很多宏,但我不认为这是真的。特别是,所有与此类运算符相关的Breeze宏都是在编译Breeze本身的过程中处理的,基本上用于生成代码。我猜这与专业化有关。
value -= 0.5 * (v.t * m * v)