Scala:“含糊的隐式值",但找不到正确的值 [英] Scala: `ambigious implicit values` but the right value is not event found

查看:96
本文介绍了Scala:“含糊的隐式值",但找不到正确的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个小型Scala程序,该程序应该:

I am writing a small Scala Program which should:

  1. 从本地FS逐行读取文件
  2. 从每一行中解析三个双精度值
  3. 根据这三个值创建案例类的实例
  4. 将这些实例传递到二进制堆

为了能够将String解析为DoubleCoordinatePoint,我想出了以下特征:

To be able to parse Strings to both Doubles and CoordinatePoints I've came up with this trait:

trait Parseable[T] {
  def parse(input: String): Either[String, T]
}

并且我为后者提供了许多类型对象实现:

and I have a number of type object implementations for the latter:

object Parseable {
  implicit val parseDouble: Parseable[Double] = new Parseable[Double] {
    override def parse(input: String): Either[String, Double] = {
      val simplifiedInput = input.replaceAll("[ \\n]", "").toLowerCase
      try Right(simplifiedInput.toDouble) catch {
        case _: NumberFormatException =>
          Left(input)
      }
    }
  }

  implicit val parseInt: Parseable[Int] = new Parseable[Int] {
    override def parse(input: String): Either[String, Int] = {
      val simplifiedInput = input.replaceAll("[ \\n]", "").toLowerCase
      try Right(simplifiedInput.toInt) catch {
        case _: NumberFormatException =>
          Left(input)
      }
    }
  }

  implicit val parseCoordinatePoint: Parseable[CoordinatePoint] = new Parseable[CoordinatePoint] {
    override def parse(input: String): Either[String, CoordinatePoint] = {
      val simplifiedInput = input.replaceAll("[ \\n]", "").toLowerCase
      val unparsedPoints: List[String] = simplifiedInput.split(",").toList
      val eithers: List[Either[String, Double]] = unparsedPoints.map(parseDouble.parse)
      val sequence: Either[String, List[Double]] = eithers.sequence
      sequence match {
        case Left(value) => Left(value)
        case Right(doublePoints) => Right(CoordinatePoint(doublePoints.head, doublePoints(1), doublePoints(2)))
      }
    }
  }
}

我有一个公共对象,该对象将调用委派给相应的隐式Parseable(在同一文件中):

I have a common object that delegates the call to a corresponding implicit Parseable (in the same file):

object InputParser {
  def parse[T](input: String)(implicit p: Parseable[T]): Either[String, T] = p.parse(input)
}

仅供参考-这是CoordinatePoint案例类:

and just for reference - this is the CoordinatePoint case class:

case class CoordinatePoint(x: Double, y: Double, z: Double)

在我的主程序中(确认文件在那里,并且不为空,等等.)我想将每一行转换为CoordinatePoint的实例,如下所示:

In my main program (after having validated that the file is there, and is not empty, etc..) I want to transform each line into an instance of CoordinatePoint as follows:

  import Parseable._
  import CoordinatePoint._

  ...
  private val bufferedReader = new BufferedReader(new FileReader(fileName))

  private val streamOfMaybeCoordinatePoints: Stream[Either[String, CoordinatePoint]] = Stream
    .continually(bufferedReader.readLine())
    .takeWhile(_ != null)
    .map(InputParser.parse(_))

我得到的错误是:

[error] /home/vgorcinschi/data/eclipseProjects/Algorithms/Chapter 2 Sorting/algorithms2_1/src/main/scala/ca/vgorcinschi/algorithms2_4/selectionfilter/SelectionFilter.scala:42:27: ambiguous implicit values:
[error]  both value parseDouble in object Parseable of type => ca.vgorcinschi.algorithms2_4.selectionfilter.Parseable[Double]
[error]  and value parseInt in object Parseable of type => ca.vgorcinschi.algorithms2_4.selectionfilter.Parseable[Int]
[error]  match expected type ca.vgorcinschi.algorithms2_4.selectionfilter.Parseable[T]
[error]     .map(InputParser.parse(_))
[error]                           ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed Sep 1, 2020 10:38:18 PM

我不明白,也不知道在哪里寻找编译器为何找到Parseable[Int]Parseable[Double],但是找不到唯一的一个-Parseable[CoordinatePoint].

I don't understand nor know where to look for why is the compiler finding Parseable[Int] and Parseable[Double] but not the only right one - Parseable[CoordinatePoint].

所以我想,好的,让我先指定一下转换函数来帮助编译器:

So I thought, ok let me give the compiler a hand by specifying the transformation function from beforehand:

  private val bufferedReader = new BufferedReader(new FileReader(fileName))

  val stringTransformer: String => Either[String, CoordinatePoint] = s => InputParser.parse(s)

  private val streamOfMaybeCoordinatePoints: Stream[Either[String, CoordinatePoint]] = Stream
    .continually(bufferedReader.readLine())
    .takeWhile(_ != null)
    .map(stringTransformer)

可惜这只是在函数声明中的代码上方产生​​了相同的错误.

Alas this yields the same error just a bit up the code - in the function declaration.

我很想知道是什么导致了这种行为.既要纠正代码又要获取个人知识.在这一点上,我很好奇.

I would love to learn what is that that causes such behavior. Both to rectify the code and for personal knowledge. At this point I am very curious.

推荐答案

一个解决方法是显式指定参数类型

One fix is to specify type prameter explicitly

InputParser.parse[CoordinatePoint](_)

另一个是优先考虑隐式.例如

Another is to prioritize implicits. For example

trait LowPriorityParseable1 {
  implicit val parseInt: Parseable[Int] = ...
}

trait LowPriorityParseable extends LowPriorityParseable1 {
  implicit val parseDouble: Parseable[Double] = ...
}

object Parseable extends LowPriorityParseable {
  implicit val parseCoordinatePoint: Parseable[CoordinatePoint] = ...
}

顺便说一句,由于您将隐式对象放入了同伴对象中,因此现在导入它们并没有多大意义.

By the way, since you put implicits into the companion object it doesn't make much sense now to import them.

在的呼叫站点

object InputParser {
  def parse[T](input: String)(implicit p: Parseable[T]): Either[String, T] = p.parse(input)
}

推断

类型参数T(如果未明确指定)不早于解析隐式(类型推断和隐式解析相互影响).否则以下代码将无法编译

type parameter T is inferred (if not specified explicitly) not before the implicit is resolved (type inference and implicit resolution make impact on each other). Otherwise the following code wouldn't compile

trait TC[A]
object TC {
  implicit val theOnlyImplicit: TC[Int] = null
}    
def materializeTC[A]()(implicit tc: TC[A]): TC[A] = tc
  
materializeTC() // compiles, A is inferred as Int

因此,在隐式解析过程中,编译器将尝试尽早推断类型(否则,在具有TC的示例中,类型A会被推断为Nothing,而不会找到隐式).顺便说一句,隐式转换是一个例外,编译器将尝试隐式转换(

So during implicit resolution compiler tries to infer types not too early (otherwise in the example with TC type A would be inferred as Nothing and implicit wouldn't be found). By the way, an exception is implicit conversions where compiler tries to infer types eagerly (sometimes this can make troubles too)

// try to infer implicit parameters immediately in order to:
//   1) guide type inference for implicit views
//   2) discard ineligible views right away instead of risking spurious ambiguous implicits

查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆