У меня есть иерархия классов, обозначающих выражения, некоторые из которых обертывают объекты других типов. Я хотел бы настроить некоторые неявные преобразования из завернутых типов в типы выражений, так что легче создавать выражения. Я попробовал 3 места, где можно определить неявные преобразования (как предложено различными источниками в Интернете): объект, который импортируется (это работает), объекты-компаньоны для типов источников (это условно работает для неперегруженных методов) и сопутствующий объект для тип цели (это не работает). Код ниже:Где/как Scala ищет неявные методы преобразования?
import scala.language.implicitConversions
// 1.
// If conversions are defined this way, the program compiles.
// But many sources insist that compiler also looks in the source/target companions for conversions.
object Conversions {
implicit def varToExpr(v: Var): VarExpr = new VarExpr(v)
implicit def constToExpr(c: Const): ConstExpr = new ConstExpr(c)
}
import Conversions._
class Var(val name: String)
class Const(val value: String)
// 2.
// These conversions do not work for e1-e4 (see below), but e5-e10 compile.
object Var {
implicit def varToExpr(v: Var): VarExpr = new VarExpr(v)
}
object Const {
implicit def constToExpr(c: Const): ConstExpr = new ConstExpr(c)
}
trait Expr {
def +(right: Expr): PlusExpr = PlusExpr(this, right)
def :+(right: Expr): PlusExpr = PlusExpr(this, right)
// 3.
// These do not work at all.
implicit def varToExpr(v: Var): VarExpr = new VarExpr(v)
implicit def constToExpr(c: Const): ConstExpr = new ConstExpr(c)
}
case class ConstExpr(c: Const) extends Expr
case class VarExpr(v: Var) extends Expr
case class PlusExpr(left: Expr, right: Expr) extends Expr
class Test {
def main(args: Array[String]) {
val a = new Var("a")
val b = new Var("b")
val c = new Const("c")
// Want to be able to write things like that.
// The first 4 work only with the 1-st way of defining the implicit conversions.
// For the 2nd way they give
// Error:(__, __) type mismatch;
// found : Const (or Var, or VarExpr)
// required: String
val e1 = a + b
val e2 = c + c
val e3 = (a + b) + c
val e4 = a + VarExpr(b)
// This compiles with both 1st and 2nd ways.
val e5 = ConstExpr(c) + b
val e6 = VarExpr(a) + c
val e7 = a :+ b
val e8 = c :+ c
val e9 = (a :+ b) :+ c
val e10 = a :+ VarExpr(b)
}
}
Я попытался определить два оператора: +
и :+
. Для +
конверсии, похоже, каким-то образом конфликтуют с конверсиями до String
, таким образом, только 1-й путь (импортированный объект) действительно работает. Для :+
сопутствующие объекты типа источника также прекрасны. Определение конверсий в сопутствующем объекте целевого типа не работает, несмотря на то, что говорят некоторые источники (например, http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html).
Вопрос в том, каким образом можно определить такие преобразования. Первый способ, по-видимому, часто используется в стандартной библиотеке (например, JavaConversions
/JavaConverters
). Но каков правильный путь?
UPD 1 Как отметил Алексей Романов, я попытался определить преобразования в trait Expr
вместо своего объекта-компаньона. Имея эти:
object Expr {
implicit def varToExpr(v: Var): VarExpr = new VarExpr(v)
implicit def constToExpr(c: Const): ConstExpr = new ConstExpr(c)
}
позволяет писать такие вещи, как
val e12: Expr = a
но не
val e11: Expr = a :+ VarExpr(b)
UPD 2 Как намекнул @cchantep, проблема с определением преобразования в компаньона объекты могут иметь приоритет. В Predef.scala
мне удалось найти:
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
То, что я понял из этого является то, что компилятор попытается использовать это определение, если +
используется на объекте типа, который сам по себе не определяет +
. Кроме того, это определение +
, по-видимому, будет иметь приоритет любого неявного преобразования, которое определено в сопутствующем объекте. Это объясняет ошибку компилятора, которую я получал.
Возможный дубликат [Scala: Неявный приоритет разрешения параметров] (http://stackoverflow.com/questions/8623055/scala-implicit-parameter-resolution-precedence) – cchantep
@cchantep Не могли бы вы рассказать подробнее? Вы предполагаете, что преобразование в 'String' для оператора' + 'имеет более высокий приоритет, чем преобразования, которые я определяю в сопутствующих объектах? Ну может быть, но я пока не могу найти, где именно это определено. –
@cchantep Это также не объясняет, почему сопутствующий объект целевого типа 'Expr' не выполняется, что предлагается в FAQ, который я задал в моем вопросе. –