Scala:for-comprehension类型推理问题的多个flatMap定义

时间:2017-12-01 17:16:45

标签: scala scala-2.11 scalac scala-compiler scala-2.12

我有这样的事情:

case class Box[A](x: A) {
  def flatMap[B](f: A => GenTraversableOnce[B]): GenTraversableOnce[B] =
    f(x)

  def flatMap[B](f: A => Box[B]): Box[B] =
    f(x)

  def map[B](f: A => B): Box[B] =
    Box(f(x))
}

object Box {
  for {
    i <- Box(0)
    j <- Box(1)
  } yield i + j
}

上面的代码在Scala 2.12.4中编译得很好,但是不能在Scala 2.11.12中编译

[error] Box.scala:10: missing parameter type
[error]     i <- Box(0)
[error]     ^
[error] one error found
[error] (core/compile:compileIncremental) Compilation failed

为什么呢?我做错了什么?

然后我尝试了:

for {
  i: Int <- Box(0)
  j: Int <- Box(1)
} yield i + j

现在令人惊讶的是,代码在Scala 2.11.12中编译,但不能在Scala 2.12.4中编译!?!

[error] Box.scala:10: pattern var i in value $anonfun is never used; `i@_' suppresses this warning
[error]     i: Int <- Box(0)
[error]     ^
[error] Box.scala:11: pattern var j in value $anonfun is never used; `j@_' suppresses this warning
[error]     j: Int <- Box(1)
[error]     ^
[error] two errors found
[error] (core/compile:compileIncremental) Compilation failed

这些是我正在使用的scalac标志:

def scalacOptionsForVersion(scalaVersion: String) = CrossVersion.partialVersion(scalaVersion) match {
  case Some((2, 10)) => Seq(
    "-deprecation",
    "-encoding", "UTF-8",
    "-feature",
    "-language:implicitConversions",
    "-language:reflectiveCalls",
    "-unchecked",
    "-Xfatal-warnings",
    "-Xlint",
    "-Yinline-warnings",
    "-Yno-adapted-args",
    "-Ywarn-dead-code",
    //"-Ywarn-numeric-widen",             // bugs in 2.10
    "-Xfuture"
  )
  case Some((2, 11)) => Seq(              // Copied from https://tpolecat.github.io/2014/04/11/scalac-flags.html
    "-deprecation",
    "-encoding", "UTF-8",                 // yes, this is 2 args
    "-feature",
    "-language:existentials",
    "-language:higherKinds",
    "-language:implicitConversions",
    "-unchecked",
    "-Xfatal-warnings",
    "-Xlint",
    "-Yno-adapted-args",
    "-Ywarn-dead-code",                  // N.B. doesn't work well with the ??? hole
    "-Ywarn-numeric-widen",
    //"-Ywarn-value-discard",            // This is broken in 2.11 for Unit types
    "-Xfuture",
    "-Ywarn-unused-import"               // 2.11 only
  )
  case Some((2, 12)) => Seq(             // Copied from https://tpolecat.github.io/2017/04/25/scalac-flags.html
    "-deprecation",                      // Emit warning and location for usages of deprecated APIs.
    "-encoding", "utf-8",                // Specify character encoding used by source files.
    "-explaintypes",                     // Explain type errors in more detail.
    "-feature",                          // Emit warning and location for usages of features that should be imported explicitly.
    "-language:existentials",            // Existential types (besides wildcard types) can be written and inferred
    "-language:experimental.macros",     // Allow macro definition (besides implementation and application)
    "-language:higherKinds",             // Allow higher-kinded types
    "-language:implicitConversions",     // Allow definition of implicit functions called views
    "-unchecked",                        // Enable additional warnings where generated code depends on assumptions.
    "-Xcheckinit",                       // Wrap field accessors to throw an exception on uninitialized access.
    "-Xfatal-warnings",                  // Fail the compilation if there are any warnings.
    "-Xfuture",                          // Turn on future language features.
    "-Xlint:adapted-args",               // Warn if an argument list is modified to match the receiver.
    "-Xlint:by-name-right-associative",  // By-name parameter of right associative operator.
    "-Xlint:constant",                   // Evaluation of a constant arithmetic expression results in an error.
    "-Xlint:delayedinit-select",         // Selecting member of DelayedInit.
    "-Xlint:doc-detached",               // A Scaladoc comment appears to be detached from its element.
    "-Xlint:inaccessible",               // Warn about inaccessible types in method signatures.
    "-Xlint:infer-any",                  // Warn when a type argument is inferred to be `Any`.
    "-Xlint:missing-interpolator",       // A string literal appears to be missing an interpolator id.
    "-Xlint:nullary-override",           // Warn when non-nullary `def f()' overrides nullary `def f'.
    "-Xlint:nullary-unit",               // Warn when nullary methods return Unit.
    "-Xlint:option-implicit",            // Option.apply used implicit view.
    "-Xlint:package-object-classes",     // Class or object defined in package object.
    "-Xlint:poly-implicit-overload",     // Parameterized overloaded implicit methods are not visible as view bounds.
    "-Xlint:private-shadow",             // A private field (or class parameter) shadows a superclass field.
    "-Xlint:stars-align",                // Pattern sequence wildcard must align with sequence component.
    "-Xlint:type-parameter-shadow",      // A local type parameter shadows a type already in scope.
    "-Xlint:unsound-match",              // Pattern match may not be typesafe.
    "-Yno-adapted-args",                 // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.
    "-Ypartial-unification",             // Enable partial unification in type constructor inference
    "-Ywarn-dead-code",                  // Warn when dead code is identified.
    "-Ywarn-extra-implicit",             // Warn when more than one implicit parameter section is defined.
    "-Ywarn-inaccessible",               // Warn about inaccessible types in method signatures.
    "-Ywarn-infer-any",                  // Warn when a type argument is inferred to be `Any`.
    "-Ywarn-nullary-override",           // Warn when non-nullary `def f()' overrides nullary `def f'.
    "-Ywarn-nullary-unit",               // Warn when nullary methods return Unit.
    "-Ywarn-numeric-widen",              // Warn when numerics are widened.
    "-Ywarn-unused:implicits",           // Warn if an implicit parameter is unused.
    "-Ywarn-unused:imports",             // Warn if an import selector is not referenced.
    "-Ywarn-unused:locals",              // Warn if a local definition is unused.
    "-Ywarn-unused:params",              // Warn if a value parameter is unused.
    "-Ywarn-unused:patvars",             // Warn if a variable bound in a pattern is unused.
    "-Ywarn-unused:privates",            // Warn if a private member is unused.
    "-Ywarn-value-discard"               // Warn when non-Unit expression results are unused.
  )
  case _ => throw new IllegalArgumentException(s"No scalacOptions found for Scala $scalaVersion")
}

如何编写此代码,以便在Scala 2.11.x和2.12.x中编译而不需要显式类型?

1 个答案:

答案 0 :(得分:5)

它并不完美,但你可以使用基于implicits的解决方案而不是重载。

import scala.collection.GenTraversableOnce

case class Box[A](x: A) {
  def flatMap[B, F[_]](f: A => F[B])(implicit fmap: Box.FlatMap[F]): F[B] =
    fmap(x)(f)

  def map[B](f: A => B) = Box(f(x))
}

object Box {
  sealed trait FlatMap[F[_]] {
    def apply[A, B](a: A)(f: A => F[B]): F[B]
  }
  implicit val GenTraversableOnceFM = new FlatMap[GenTraversableOnce] {
    def apply[A, B](a: A)(f: A => GenTraversableOnce[B]) = f(a)
  }
  implicit val BoxFM = new FlatMap[Box] {
    def apply[A, B](a: A)(f: A => Box[B]) = f(a)
  }
}