对不兼容的类型(Double和DenseVector)使用-=会导致奇怪的Scala编译器错误

时间:2020-11-09 14:00:52

标签: 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

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

我当前正在观察的内容:

MWE:

import breeze.linalg._
object MWE {
  def test(v: DenseVector[Double], m: DenseMatrix[Double]): Unit = {
    var value = 0.5
    value += v
    value += 0.5 * v.t * m * v
 }
}

错误地在该行的Double +=和DenseVector value上使用了运算符v

    value += 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

相反,IDE错误地使用运算符-=并以红色突出显示了它。 尝试编译它时,我得到了一个不同但仍有用的信息:

value -= is not a member of Double
  Expression does not convert to assignment because:
    type mismatch;
     found   : breeze.linalg.DenseVector[Double]
     required: Double
    expansion: value = value.<$minus: error>(v)
    value -= v

第一个问题:为什么这两种情况表现出不同的行为? 我对scala中的这些运算符进行了简短的了解(请参见Where is Scala's += defined in the context of Int?),并在查看了scala文档(尤其是https://www.scala-lang.org/api/2.13.3/scala/Double.html)之后,没有发现+=的任何原因和-=会被区别对待?

回到我们的原始代码,行为上的差异以另一种方式体现出来:

在行中使用+=

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 

第二个问题:这到底该告诉我什么,为什么会发生?



除了这些主要问题以外,还有一些上下文信息和注释:

又回到了我遇到此问题的原始代码和设置,我们实际上使用的是Scala 2.11.11而不是2.13.3(因此使用Breeze 0.13.2而不是Breeze 1.1)。 这导致编译器根本不显示任何有用的信息,而是在编译器中产生了NullPointerException:

scalac: Error: org.jetbrains.jps.incremental.scala.remote.ServerException
java.lang.NullPointerException
    at scala.tools.nsc.transform.Mixin$MixinTransformer.scala$tools$nsc$transform$Mixin$MixinTransformer$$postTransform(Mixin.scala:1154)
    at scala.tools.nsc.transform.Mixin$MixinTransformer$$anonfun$transform$1.apply(Mixin.scala:1261)
    at scala.tools.nsc.transform.Mixin$MixinTransformer$$anonfun$transform$1.apply(Mixin.scala:1261)
    at scala.reflect.internal.SymbolTable.enteringPhase(SymbolTable.scala:235)
    at scala.reflect.internal.SymbolTable.exitingPhase(SymbolTable.scala:256)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1261)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1347)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1386)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:176)
    at scala.collection.immutable.List.mapConserve(List.scala:200)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1363)
    at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1361)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1360)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:176)
    at scala.collection.immutable.List.mapConserve(List.scala:200)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1404)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
    at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1408)
    at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1407)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1406)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:176)
    at scala.collection.immutable.List.mapConserve(List.scala:200)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1426)
    at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1426)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1425)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1258)
    at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
    at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:147)
    at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$applyPhase$1.apply$mcV$sp(Global.scala:467)
    at scala.tools.nsc.Global$GlobalPhase.withCurrentUnit(Global.scala:458)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:467)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:425)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:425)
    at scala.collection.Iterator$class.foreach(Iterator.scala:891)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1334)
    at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:425)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1528)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1513)
    at scala.tools.nsc.Global$Run.compileSources(Global.scala:1508)
    at scala.tools.nsc.Global$Run.compile(Global.scala:1609)
    at xsbt.CachedCompiler0.run(CompilerInterface.scala:130)
    at xsbt.CachedCompiler0.run(CompilerInterface.scala:105)
    at xsbt.CompilerInterface.run(CompilerInterface.scala:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sbt.internal.inc.AnalyzingCompiler.call(AnalyzingCompiler.scala:237)
    at sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:111)
    at sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:90)
    at org.jetbrains.jps.incremental.scala.local.IdeaIncrementalCompiler.compile(IdeaIncrementalCompiler.scala:42)
    at org.jetbrains.jps.incremental.scala.local.LocalServer.compile(LocalServer.scala:43)
    at org.jetbrains.jps.incremental.scala.remote.Main$.compileLogic(Main.scala:145)
    at org.jetbrains.jps.incremental.scala.remote.Main$.$anonfun$handleCommand$1(Main.scala:131)
    at org.jetbrains.jps.incremental.scala.remote.Main$.decorated$1(Main.scala:121)
    at org.jetbrains.jps.incremental.scala.remote.Main$.handleCommand(Main.scala:128)
    at org.jetbrains.jps.incremental.scala.remote.Main$.serverLogic(Main.scala:105)
    at org.jetbrains.jps.incremental.scala.remote.Main$.nailMain(Main.scala:63)
    at org.jetbrains.jps.incremental.scala.remote.Main.nailMain(Main.scala)
    at sun.reflect.GeneratedMethodAccessor6.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.martiansoftware.nailgun.NGSession.run(NGSession.java:319)

由于我完全没有暗示NPE的起因,因此需要一些时间来缩小范围。 NPE似乎是scala编译器中的错误,该错误已在2.13中修复,请参见例如。 https://github.com/scala/bug/issues/8583(如果在此处建立连接有误,请纠正我吗?)。

实际上导致这些整体出现的还有一些怪异之处(不,我不希望能够从Double xD中减去DenseVectors)。 初始代码中的操作要复杂一些,但本质上可以归结为MWE中的这一行:

value -= 0.5 * v.t * m * v

实际上,这根本不会产生DenseVector!

撇开0.5,我们将转置的向量,方矩阵和向量相乘,它们都具有相同的尺寸。此操作将产生标量,而不是向量!

但是0.5会在代码中造成混乱(至少在类型方面如此)-将标量乘以转置的DenseVector(0.5 * v.t)不会产生预期的转置的DenseVector,结果是类型DenseMatrix。然后,这会导致整行导致DenseVector的类型错误,而不是Double。

侧问题::为什么代码显示有关类型的这种行为?任何人都知道这是否是一个合理的选择,或者甚至可能是Breeze中的错误?

所以事实证明,我们的原始代码可以很容易地通过添加括号来固定:

value -= 0.5 * (v.t * m * v)

正确地添加了两个Double,一切正常。

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

1 个答案:

答案 0 :(得分:1)

第一个问题:这是有根据的猜测。 Scala对++=的处理有些怪异,因为Scala可以让您(x: Any) + (y: String)进行操作,而且我已经看到了由此引起的不同运算符的错误怪异。

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

侧面问题:这是Breeze中的限制/错误。 Scala将.5 * v.t * m * v解析为0.5.*(v.t).*(m).*(v)(即,从左到右)。 v.tTranspose[DenseVector[Double]](即行向量),说实话,Transpose的东西并没有我想要的完全烘焙。从历史上讲,我们将行向量表示为矩阵,但我怀疑它会回溯到那,因此它看到矩阵*矩阵*向量当然是向量。

相关问题