尝试传递函数时Scala类型不匹配

时间:2014-11-19 19:16:32

标签: scala types type-conversion pattern-matching

我需要一些帮助,试图弄清楚如何重用模式匹配,我宁愿不重复(如果可能的话)。我在这里和谷歌进行了搜索,试验了暗示和差异但到目前为止还没有结果。

下面是两个方法,doSomething和doSomethingElse在Ids上包含相同的模式匹配。我希望通过传入一个函数来重用该模式。

这是初始设置。 (toPath和take2的实际实现并不真正相关。)

import java.nio.file.{Paths, Path}
import java.util.UUID

def take2(x: Long): String = {
    (x % 100).toString.padTo(2, '0')
}

def take2(u: UUID): String = {
    u.toString.take(2)
}

def toPath(x: Long): Path = {
    Paths.get(s"$x")
}

def toPath(u: UUID): Path = {
    Paths.get(u.toString)
}

case class Ids(id1: Option[Long], id2: Option[UUID])

def doSomething(ids: Ids): String = ids match {
    case Ids(_, Some(uuid)) => take2(uuid)
    case Ids(Some(long), _) => take2(long)
}

def doSomethingElse(ids: Ids) = ids match {
    case Ids(_, Some(uuid)) => toPath(uuid)
    case Ids(Some(long), _) => toPath(long)
}

doSomething(Ids(Some(12345L), None))
doSomethingElse(Ids(Some(12345L), None))

我想要的是这样的事情:

def execute[A](ids: Ids)(f: Any => A): A = ids match {
    case Ids(_, Some(uuid)) => f(uuid)
    case Ids(Some(long), _) => f(long)
}

def _doSomething(ids: Ids) = execute[String](ids)(take2)
//def _doSomething2(ids: Ids) = execute[Path](ids)(toPath)

我得到的错误是:

Error: ... type mismatch;
 found   : (u: java.util.UUID)String <and> (x: Long)String
 required: Any => String
def _doSomething(ids: Ids) = execute[String](ids)(take2)
                                              ^                                          ^

如何让这些功能类型工作?

我的Scala版本2.11.2。

我一直在使用的工作表: https://github.com/lhohan/scala-pg/blob/0f1416a6c1d3e26d248c0ef2de404bab76ac4e57/src/main/scala/misc/MethodPassing.sc

非常感谢任何帮助或指示。

1 个答案:

答案 0 :(得分:1)

问题是您有两个不同的方法只是发生以共享相同的名称,例如“TAKE2”。当您尝试使用take2时,您当然没有提供可以处理任何参数类型的函数(如Any => A的要求);你甚至无法处理你想要的两种类型,因为它们是两种不同的方法!

在原始匹配语句中,您没有注意到这两个方法是两个共享相同名称的方法,因为编译器根据参数类型填写了正确的方法。没有一个功能说“插入我提供的名称然后坚持不同的方法”。 (好吧,你可以用宏来做,但是为了避免一点点重复,这非常复杂。)

现在编译器 足够智能,可以用你想要的方法创建一个函数。所以,如果你写了

def execute[A](ids: Ids)(f1: UUID => A, f2: Long => A): A = ids match {
  case Ids(_, Some(uuid)) => f1(uuid)
  case Ids(Some(long), _) => f2(long)
}

然后你可以

def doSomething(ids: Ids) = execute[String](ids)(take2, take2)

会减少重复次数。

或者,你可以写

import scala.util._
def take2(x: Long): String = (x % 100).toString.padTo(2, '0')
def take2(u: UUID): String = u.toString.take(2)
def take2(ul: Either[UUID, Long]): String = ul match {
  case Left(u) => take2(u)
  case Right(l) => take2(l)
}

(如果你在REPL中尝试这一点,请务必使用:paste,这样所有三个一起定义。)

然后你写

def execute[A](ids: Ids)(f: Either[UUID, Long] => A): A = ids match {
  case Ids(_, Some(uuid)) => f(Left(uuid))
  case Ids(Some(long), _) => f(Right(long))
}

将使用三个take2中的正确一个。 (与额外的参数装箱相关联会产生运行时损失,但我怀疑这是一个性能关键的代码路径。)

还有其他选项 - 例如,Shapeless提供了联合类型。或者你可以进行运行时模式匹配,如果你传递的信息既不是UUID也不是Long,就会抛出异常......但是这可能会导致以后遇到麻烦。