Scala方法重载用于泛型类型的特殊情况

时间:2015-02-21 16:15:53

标签: scala

我有以下方法:

def foo[A](x: =>A) = ???

def foo[B](x: =>WrapperType[B]) = ???

这不能编译,这是有道理的,因为在第一种方法中,A也可以代表WrapperType[B]。我可以在运行时检查x的类型,但似乎应该有一种在编译时这样做的方法。我怎样才能克服这个问题?

注意:上面的代码是我的真实代码的简化,它是:

def ifEmpty[B](errors:Seq[Error]*)(right: =>B): Either[Seq[Error], B] =
  if( areAllEmpty(errors) ) Right(right)
  else                      Left(errors.flatten)

def ifEmpty[C](errors:Seq[Error]*)(right: =>Either[Seq[Error], C]): Either[Seq[Error], C] =
  if( areAllEmpty(errors) ) right
  else                      Left(errors.flatten)

3 个答案:

答案 0 :(得分:5)

这是消除签名歧义的通常方法(黑客):

scala> :pa
// Entering paste mode (ctrl-D to finish)

object Y {
def f[A](a: =>  A) = 1
def f[A](a: => Either[Exception, A])(implicit d: DummyImplicit) = 2
}

// Exiting paste mode, now interpreting.

defined object Y

scala> Y.f(42)
res0: Int = 1

scala> Y.f(Right(42))
res1: Int = 2

你的foo擦除了相同的签名,因为params变成了引擎盖下的功能。

但是,对于Scala,foo(Wrapper)foo(A)更具体。

您的示例实际代码无法工作,因为它是双重定义:重载仅考虑第一个参数列表。

编辑:不要对其进行测试this way.

答案 1 :(得分:2)

有一种方法,但它涉及某种类型的hackery并可能将您的逻辑定位在远离方法调用的位置:

def foo[A](f: => A)(implicit switch: Switch[A]) = switch(f)

通过这个,您现在可以在变量f的返回类型上切换逻辑。

trait Switch[A]{
  def apply(f: => A): Whatever
}

object Switch extends LowPrioritySwitch{
  def apply[A](implicit switch: Switch[A]) = switch

  implicit def wrapped[B] = new Switch[WrappedArray[B]]{
    def apply(f: => WrappedArray[B]) = ...
  }
}

trait LowPrioritySwitch{
  implicit def any[A] = new Switch[A]{
    def apply(f: => A) = ...
  }
}

所有这一切都是为了利用隐式解决方案。也就是说,在尝试匹配特征中找到的下一个隐式之前,编译器将尝试匹配对象定义中的第一个隐式。因此这个名字。

答案 2 :(得分:0)

最后,我能够解决它,从每个答案中借鉴一个想法:

def ifEmpty[A](errors:Seq[Error]*)(implicit magnet:IfEmptyMagnet[A]): Either[Seq[Error], A] = magnet(errors:_*)

trait IfEmptyMagnet[+A] {
  def areAllEmpty(seqs: Seq[Any]*) = seqs.forall(_.size == 0)
  def apply(errors:Seq[Error]*): Either[Seq[Error], A]
}

implicit def ifEmptyMagnet[A](a: =>  A) = new IfEmptyMagnet[A]{
  def apply(errors:Seq[Error]*): Either[Seq[Error], A] = {
    if( areAllEmpty(errors) ) Right(a)
    else                      Left(errors.flatten)
  }
}

implicit def ifEmptyMagnet[A](a: => Either[Seq[Error], A])(implicit d: DummyImplicit) = new IfEmptyMagnet[A]{
  def apply(errors:Seq[Error]*): Either[Seq[Error], A] = {
    if( areAllEmpty(errors) ) a
    else                      Left(errors.flatten)
  }
}