【问题标题】:Scala type mismatch while trying to pass a function尝试传递函数时Scala类型不匹配
【发布时间】:2015-01-17 11:01:19
【问题描述】:

我需要一些帮助来尝试弄清楚如何重用我不想重复的模式匹配(如果可能的话)。我在这里和谷歌搜索过,尝试了隐式和方差,但到目前为止没有结果。

下面是 2 个方法,doSomething 和 doSomethingElse,它们在 Id 上包含相同的模式匹配。我想通过传入一个函数来重用该模式。

这是初始设置。 (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

感谢任何帮助或指点。

【问题讨论】:

    标签: scala types type-conversion pattern-matching


    【解决方案1】:

    问题是您有两个 不同 方法,它们只是 发生 共享相同的名称,例如“采取2”。当您尝试使用 take2 时,您肯定没有提供可以处理 any 参数类型的函数(如 Any =&gt; A 要求的那样);你甚至不能处理你想要的两种类型,因为它们是两种不同的方法!

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

    现在编译器足够聪明,可以用你想要的方法生成一个函数。所以如果你写了

    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))
    }
    

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

    还有其他选项——例如,Shapeless 提供联合类型。或者,如果您传递的内容既不是 UUID 也不是 Long...,那么您也可以进行运行时模式匹配并抛出异常……但这可能会在以后带来麻烦。

    【讨论】:

      猜你喜欢
      • 2018-06-28
      • 2015-09-23
      • 1970-01-01
      • 1970-01-01
      • 2017-04-26
      • 2021-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多