Scala多重继承:在方法参数中区分Iterable和PartialFunction

时间:2014-12-03 00:07:42

标签: scala multiple-inheritance

如果参数是Iterable[T1] vs函数,我希望能够定义具有相同名称且具有不同实现的方法:T1 => T2

但是,许多实现Iterable的类也实现了PartialFunction

例如:

object FunList {
  def foo(itr: Iterable[Int]) = println("hello")
  def foo(f: (Int => Int)) = println("Goodbye")
}

scala> FunList.foo(List(1))
<console>:9: error: ambiguous reference to overloaded definition,
both method foo in object FunList of type (f: Int => Int)Unit
and  method foo in object FunList of type (itr: Iterable[Int])Unit
match argument types (List[Int])
          FunList.foo(List(1))

目前我的解决方案看起来像这样,但它不匹配Iterable的子类,它们也不是PartialFunction的子类

case class SeqOrFun[T1, T2](f: (T1 => T2))
implicit def seqOrFun[T1, T2](f: (T1 => T2)) = SeqOrFun(f)

def unfurl[T1: Numeric, T2: Numeric](x: SeqOrFun[T1, T2], y: SeqOrFun[T1, T2]) = {
  (x.f, y.f) match {
    case (xs: Iterable[T1], ys: Iterable[T2]) => (xs, ys)
    case (xf: (T2 => T1), ys: Iterable[T2]) => (ys.map(xf), ys)
    case (xs: Iterable[T1], yf: (T1 => T2)) => (xs, xs.map(yf))
  }
}

1 个答案:

答案 0 :(得分:3)

由于List同时属于Iterable[Int]Int => Int,正如您所说,您所写的内容本质上是无法辨认的。也许我误读了,但是我没有看到您指定是否希望FunList.foo(List(1))打印"hello""Goodbye"的任何地方。

首先,您可以在呼叫站点使用演员表。我假设你已经知道这一点,但为了讨论起见只是为了明确:

FunList.foo(List(1): Int => Int) // "hello"
FunList.foo(List(1): Iterable[Int]) // "Goodbye"

如果我们的目标是允许来电者简单地写FunList.foo(List(1)),您可以使用magnet pattern。这意味着你根本不使用方法重载;相反,你编写一个方法,并使用隐式转换完成调度。

sealed trait FooMagnet
case class HelloMagnet(itr: Iterable[Int]) extends FooMagnet
case class GoodbyeMagnet(f: Int => Int) extends FooMagnet

def foo(x: FooMagnet): Unit = x match {
  case HelloMagnet(itr) => println("hello")
  case GoodbyeMagnet(f) => println("Goodbye")
}

def a(): Unit = {
  implicit def listIsHelloMagnet(x: List[Int]): FooMagnet = HelloMagnet(x)
  FunList.foo(List(1)) // "hello"
}

def b(): Unit = {
  implicit def listIsGoodbyeMagnet(x: List[Int]): FooMagnet = GoodbyeMagnet(x)
  FunList.foo(List(1)) // "Goodbye"
}

您在这里获得的有趣好处是,调度决策与foo实现分离,因为它由您可以在任何地方定义的那些含义决定。

您也可以使用它来解决歧义问题!从前面的两个含义开始:

implicit def iterableIsHelloMagnet(x: Iterable[Int]): FooMagnet = HelloMagnet(x)
implicit def functionIsGoodbyeMagnet(x: Int => Int): FooMagnet = GoodbyeMagnet(x)

然后专门为List[Int]添加另一个隐含。

implicit def listIsHelloMagnet(x: List[Int]): FooMagnet = HelloMagnet(x)

Scala非常聪明地看到第三次转换比前两次转换更具体,所以即使它们都适用,它也会使用List[Int]。因此,我们现在可以写:

FunList.foo(Set(1)) // "hello"
FunList.foo((_: Int) + 1) // "Goodbye"
FunList.foo(List(1)) // "hello"

你可以将那些implicits的定义移动到被调用的库中,所以调用者所要做的就是导入它们。