scala - 为什么不能推断泛型函数的参数类型?

标签 scala generics

在 Scala 2.11.7 中,具有以下 case 类和一个额外的 apply方法:

case class FieldValidator[T](key: String, isValid: T => Boolean, 
                             errorMessage: Option[String] = None)

object FieldValidator {
  def apply[T](key: String, isValid: T => Boolean, 
               errorMessage: String): FieldValidator[T] = ???
}

当我尝试使用:
FieldValidator[String](key, v => !required || v.nonEmpty, "xxx")

我收到指向 v 的“缺少参数类型”编译错误.

当我明确指定 v 的类型时,它编译得很好,我什至可以跳过 apply 的泛型类型方法,即
FieldValidator(key, (v: String) => !required || v.nonEmpty, "xxx")

为什么不是v的类型当只是 apply 的泛型类型时推断提供?

最佳答案

这不是关于泛型,而是重载和默认参数的问题。

首先,回想一下,自从 FieldValidatorcase -class,合成工厂方法

def apply(
  key: String,
  isValid: T => Boolean, 
  errorMessage: Option[String] = None
)

自动添加到伴随对象 FieldValidator .这导致 Field验证器具有两个具有默认参数和相同名称的通用方法。

这是一个较短的示例,其行为方式大致相同:
def foo[A](f: A => Boolean, x: Int = 0): Unit =  {}
def foo[A](f: A => Boolean, x: String): Unit =  {}

foo[String](_.isEmpty)

结果是:

error: missing parameter type for expanded function ((x$1: ) => x$1.isEmpty)

 foo[String](_.isEmpty)
             ^


我无法确定到底出了什么问题,但本质上,您通过向编译器抛出三种不同类型的多态性而混淆了太多的歧义:
  • 重载:您有两个名称为 apply 的方法
  • 泛型:你的方法有一个泛型类型参数 [A]
  • 默认参数:您的 errorMessage ( x 在我较短的例子中)可以省略。

  • 总之,这使编译器可以在两个同名方法之间进行选择,这些方法具有不清楚的类型和不清楚的预期类型参数数量。虽然灵活性是好的,但太多的灵活性就是太多了,编译器放弃试图弄清楚你想要什么,并强制你明确指定每个参数的所有类型,而不依赖于推理。

    从理论上讲,它可以在这种特殊情况下解决这个问题,但这需要更复杂的推理算法和更多的回溯和反复试验(这会减慢一般情况下的编译速度)。你不希望编译器花半天时间玩 typesystem-sudoku,即使它理论上可以找到一个独特的解决方案。快速退出并显示错误消息是一个合理的选择。

    解决方法

    作为一种简单的解决方法,请考虑以允许编译器尽快消除歧义的方式重新排序参数。例如,将参数拆分为两个参数列表,其中两个 String s 首先出现,将使其明确:
    case class FieldValidator[T](
      key: String,
      isValid: T => Boolean, 
      errorMessage: Option[String] = None
    )
    
    object FieldValidator {
      def apply[T]
        (key: String, errorMessage: String)
        (isValid: T => Boolean)
      : FieldValidator[T] = {
        ???
      }
    }
    
    val f = FieldValidator[String]("key", "err"){
      s => s.nonEmpty
    }
    

    关于scala - 为什么不能推断泛型函数的参数类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54347455/

    相关文章:

    scala - 如何将 sbt pack 插件导入到项目中

    java - 整数最小/最大值特殊溢出行为

    generics - kotlin 泛型和运算符重载

    c# - 在同一个类上多次使用通用接口(interface)

    generics - 如何使结构字段成为泛型类型

    list - 使用 scala.util.Random 在 Set 和 List 上进行随机播放的行为

    java - 使用前瞻表达式匹配组

    java - Java内部类写类型参数

    scala - Spark Streaming + Elasticsearch:无法解析符号saveToEs

    c# - 为什么这个类型转换不起作用?