如何在没有歧义的情况下用不同的证据重载泛型方法?
How to overload generic method with different evidence without ambiguity?
我有一个带有两个参数的自定义 compare
方法。其中一个应该可以隐式转换为另一个:
object Test extends App {
def compare[T1, T2](a: T1, b: T2)(implicit ev: T1 => T2) = compareImpl[T2](ev(a), b)
def compare[T1, T2](a: T1, b: T2)(implicit ev: T2 => T1) = compareImpl[T1](a, ev(b))
def compareImpl[T](a: T, b: T) = a == b
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
println(compare(Foo("hello"), Bar("hello")))
}
但是这个片段给我错误:
error: ambiguous reference to overloaded definition,
both method compare in object Test of type [T1, T2](a: T1, b: T2)(implicit ev: T2 => T1)Boolean
and method compare in object Test of type [T1, T2](a: T1, b: T2)(implicit ev: T1 => T2)Boolean
match argument types (Test.Foo,Test.Bar) and expected result type Any
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
如果我删除第二个 compare
,它可以工作,但是如果我删除 compare(Bar("hello), Foo("hello"))
,它就无法编译。
这两个版本怎么才能不歧义呢?
这里的问题是因为你的两个 compare
函数都具有完全相同的类型参数,这对于 Scala 编译器来说是不明确的,无法确定使用哪个。
例如,当您比较 compare[Foo, Bar]
时,Scala 编译器不清楚它应该将 compare
函数与 (implicit ev: T1 => T2)
一起使用,还是第二个函数与 [=14] 一起使用=] ,因为 Foo
和 Bar
都可以放置为 T1
或 T2
。
事实上,这就是当您删除其中一个比较功能时它起作用的原因。因为 compare
函数没有重载版本,所以 Foo
和 Bar
可以作为 T1
和 T2
放置在你唯一的 compare
中功能。
这是另一个 Whosebug 问题的答案,该问题与您的问题有某种关联,并详细描述了问题:
我最终使用了宏,因为目前 Scala 没有类型 lambda 并且它执行泛型类型擦除,所以开箱即用不支持这样的东西。
宏定义:
import scala.reflect.runtime.universe._
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
import scala.language.implicitConversions
def compare[T1, T2](a: T1, b: T2): Boolean = macro compareImpl[T1,T2]
def compareImpl[T1: c.WeakTypeTag, T2: c.WeakTypeTag](c: Context)(a: c.Expr[T1], b: c.Expr[T2]): c.Expr[Boolean] = {
import c.universe._
// Search for T1=>T2 first. If not found, search T2=>T1
val f1 = c.inferImplicitValue(c.weakTypeOf[T1 => T2])
if (f1.isEmpty) {
val f2 = c.inferImplicitValue(c.weakTypeOf[T2 => T1])
if(f2.isEmpty) {
c.abort(c.enclosingPosition, s"Cannot find ${weakTypeOf[T1]}=> ${weakTypeOf[T2]}")
}
else {
c.Expr(q"$f2.apply($b) == $a")
}
}
else {
c.Expr(q"$f1.apply($a) == $b")
}
}
测试:
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
println(compare(Foo("hello"), Bar("hello")))
println(compare(Bar("hello"), Foo("hello")))
没有宏的解决方案(它基于类型类)
def compare[T1, T2](a: T1, b: T2)(implicit cmp: Compare[T1, T2]) = (compareImpl[cmp.T] _).tupled(cmp(a, b))
def compareImpl[T](a: T, b: T) = a == b
trait Compare[T1, T2] {
type T
type Out = (T, T)
def apply(a: T1, b: T2): Out
}
object Compare {
type Aux[T1, T2, T0] = Compare[T1, T2] { type T = T0 }
def instance[T1, T2, T0](f: (T1, T2) => (T0, T0)): Aux[T1, T2, T0] = new Compare[T1, T2] {
override type T = T0
override def apply(a: T1, b: T2): Out = f(a, b)
}
implicit def directCompare[T1, T2](implicit ev: T1 => T2): Aux[T1, T2, T2] = instance((a, b) => (ev(a), b))
implicit def reverseCompare[T1, T2](implicit ev: T2 => T1): Aux[T1, T2, T1] = instance((a, b) => (a, ev(b)))
}
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
println(compare(Foo("hello"), Bar("hello"))) // true
或者,如果需要,您甚至可以优先考虑正向和反向方向
def compare[T1, T2](a: T1, b: T2)(implicit cmp: Compare[T1, T2]) = (compareImpl[cmp.T] _).tupled(cmp(a, b))
def compareImpl[T](a: T, b: T) = a == b
trait Compare[T1, T2] {
type T
type Out = (T, T)
def apply(a: T1, b: T2): Out
}
trait LowPriorityCompare {
type Aux[T1, T2, T0] = Compare[T1, T2] { type T = T0 }
def instance[T1, T2, T0](f: (T1, T2) => (T0, T0)): Aux[T1, T2, T0] = new Compare[T1, T2] {
override type T = T0
override def apply(a: T1, b: T2): Out = f(a, b)
}
implicit def reverseCompare[T1, T2](implicit ev: T2 => T1): Aux[T1, T2, T1] = instance((a, b) => (a, ev(b)))
}
object Compare extends LowPriorityCompare {
implicit def directCompare[T1, T2](implicit ev: T1 => T2): Aux[T1, T2, T2] = instance((a, b) => (ev(a), b))
}
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
implicit def bar2foo(f: Bar): Foo = Foo(f.s)
println(compare(Foo("hello"), Bar("hello"))) // true
我有一个带有两个参数的自定义 compare
方法。其中一个应该可以隐式转换为另一个:
object Test extends App {
def compare[T1, T2](a: T1, b: T2)(implicit ev: T1 => T2) = compareImpl[T2](ev(a), b)
def compare[T1, T2](a: T1, b: T2)(implicit ev: T2 => T1) = compareImpl[T1](a, ev(b))
def compareImpl[T](a: T, b: T) = a == b
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
println(compare(Foo("hello"), Bar("hello")))
}
但是这个片段给我错误:
error: ambiguous reference to overloaded definition,
both method compare in object Test of type [T1, T2](a: T1, b: T2)(implicit ev: T2 => T1)Boolean
and method compare in object Test of type [T1, T2](a: T1, b: T2)(implicit ev: T1 => T2)Boolean
match argument types (Test.Foo,Test.Bar) and expected result type Any
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
如果我删除第二个 compare
,它可以工作,但是如果我删除 compare(Bar("hello), Foo("hello"))
,它就无法编译。
这两个版本怎么才能不歧义呢?
这里的问题是因为你的两个 compare
函数都具有完全相同的类型参数,这对于 Scala 编译器来说是不明确的,无法确定使用哪个。
例如,当您比较 compare[Foo, Bar]
时,Scala 编译器不清楚它应该将 compare
函数与 (implicit ev: T1 => T2)
一起使用,还是第二个函数与 [=14] 一起使用=] ,因为 Foo
和 Bar
都可以放置为 T1
或 T2
。
事实上,这就是当您删除其中一个比较功能时它起作用的原因。因为 compare
函数没有重载版本,所以 Foo
和 Bar
可以作为 T1
和 T2
放置在你唯一的 compare
中功能。
这是另一个 Whosebug 问题的答案,该问题与您的问题有某种关联,并详细描述了问题:
我最终使用了宏,因为目前 Scala 没有类型 lambda 并且它执行泛型类型擦除,所以开箱即用不支持这样的东西。
宏定义:
import scala.reflect.runtime.universe._
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
import scala.language.implicitConversions
def compare[T1, T2](a: T1, b: T2): Boolean = macro compareImpl[T1,T2]
def compareImpl[T1: c.WeakTypeTag, T2: c.WeakTypeTag](c: Context)(a: c.Expr[T1], b: c.Expr[T2]): c.Expr[Boolean] = {
import c.universe._
// Search for T1=>T2 first. If not found, search T2=>T1
val f1 = c.inferImplicitValue(c.weakTypeOf[T1 => T2])
if (f1.isEmpty) {
val f2 = c.inferImplicitValue(c.weakTypeOf[T2 => T1])
if(f2.isEmpty) {
c.abort(c.enclosingPosition, s"Cannot find ${weakTypeOf[T1]}=> ${weakTypeOf[T2]}")
}
else {
c.Expr(q"$f2.apply($b) == $a")
}
}
else {
c.Expr(q"$f1.apply($a) == $b")
}
}
测试:
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
println(compare(Foo("hello"), Bar("hello")))
println(compare(Bar("hello"), Foo("hello")))
没有宏的解决方案(它基于类型类)
def compare[T1, T2](a: T1, b: T2)(implicit cmp: Compare[T1, T2]) = (compareImpl[cmp.T] _).tupled(cmp(a, b))
def compareImpl[T](a: T, b: T) = a == b
trait Compare[T1, T2] {
type T
type Out = (T, T)
def apply(a: T1, b: T2): Out
}
object Compare {
type Aux[T1, T2, T0] = Compare[T1, T2] { type T = T0 }
def instance[T1, T2, T0](f: (T1, T2) => (T0, T0)): Aux[T1, T2, T0] = new Compare[T1, T2] {
override type T = T0
override def apply(a: T1, b: T2): Out = f(a, b)
}
implicit def directCompare[T1, T2](implicit ev: T1 => T2): Aux[T1, T2, T2] = instance((a, b) => (ev(a), b))
implicit def reverseCompare[T1, T2](implicit ev: T2 => T1): Aux[T1, T2, T1] = instance((a, b) => (a, ev(b)))
}
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
println(compare(Foo("hello"), Bar("hello"))) // true
或者,如果需要,您甚至可以优先考虑正向和反向方向
def compare[T1, T2](a: T1, b: T2)(implicit cmp: Compare[T1, T2]) = (compareImpl[cmp.T] _).tupled(cmp(a, b))
def compareImpl[T](a: T, b: T) = a == b
trait Compare[T1, T2] {
type T
type Out = (T, T)
def apply(a: T1, b: T2): Out
}
trait LowPriorityCompare {
type Aux[T1, T2, T0] = Compare[T1, T2] { type T = T0 }
def instance[T1, T2, T0](f: (T1, T2) => (T0, T0)): Aux[T1, T2, T0] = new Compare[T1, T2] {
override type T = T0
override def apply(a: T1, b: T2): Out = f(a, b)
}
implicit def reverseCompare[T1, T2](implicit ev: T2 => T1): Aux[T1, T2, T1] = instance((a, b) => (a, ev(b)))
}
object Compare extends LowPriorityCompare {
implicit def directCompare[T1, T2](implicit ev: T1 => T2): Aux[T1, T2, T2] = instance((a, b) => (ev(a), b))
}
case class Foo(s: String)
case class Bar(s: String)
implicit def foo2bar(f: Foo): Bar = Bar(f.s)
implicit def bar2foo(f: Bar): Foo = Foo(f.s)
println(compare(Foo("hello"), Bar("hello"))) // true