Scala: несколько определений flatMap для решения проблемы вывода типа для понимания

У меня есть что-то вроде этого:

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, не требуя явных типов?


person pathikrit    schedule 01.12.2017    source источник


Ответы (1)


Это не идеально, но вы можете использовать неявное решение вместо перегрузки.

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)
  }
}
person Jasper-M    schedule 01.12.2017
comment
Это здорово и это работает! В образовательных целях, что не так с кодом, который я разместил? Почему одна версия работает в Scala 2.11, но не работает в Scala 2.12, а другая работает в Scala 2.12, но не работает в Scala 2.11? - person pathikrit; 01.12.2017
comment
Я думаю, что часть неработающей части была вызвана -Xfatal-warnings. (Вы можете попробовать использовать Scala 2.12 с -souce 2.11.) - person Gábor Bakos; 01.12.2017
comment
Я считаю, что 2.12 содержит улучшенный вывод типов для перегруженных методов более высокого порядка. Думаю, это было необходимо из-за включения типов ЗРК, но подробностей не знаю. Сбой в версии 2.12 был вызван (как мне кажется) ошибкой с -Xlint, ставшей фатальной из-за -Xfatal-warnings. - person Jasper-M; 01.12.2017
comment
Ошибка также может быть связана с -Ywarn-unused. Я не специалист по флагам. - person Jasper-M; 01.12.2017
comment
Флаг в вашем списке -Ywarn-unused:patvars. patvars и params не включаются только -Xlint. - person som-snytt; 01.12.2017