简体   繁体   English

为什么不推断通用函数的参数类型?

[英]Why is the parameter type of a generic function not inferred?

In Scala 2.11.7, having the following case class and an additional apply method: 在Scala 2.11.7中,具有以下案例类和附加的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] = ???
}

when I try to use: 当我尝试使用时:

FieldValidator[String](key, v => !required || v.nonEmpty, "xxx")

I'm getting a "missing parameter type" compilation error pointing at v . 我收到一个指向v的“缺少参数类型”编译错误。

When I explicitly specify the type of v , it compiles fine, and I can even skip the generic type of the apply method, ie 当我明确指定v的类型时,它编译得很好,我甚至可以跳过apply方法的泛型类型,即

FieldValidator(key, (v: String) => !required || v.nonEmpty, "xxx")

Why isn't the type of v inferred when just the generic type of apply is provided? 当提供通用类型的apply时,为什么不推断v的类型?

It's not so much about generics, it's rather a problem with overloading and default parameters. 它不是泛型,而是重载和默认参数的问题。

First, recall that since FieldValidator is a case -class, a synthetic factory method 首先,回想一下,因为FieldValidator是一个case -class,一个合成工厂方法

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

is automatically added to the companion object FieldValidator . 自动添加到伴随对象FieldValidator This results in Field validator having two generic methods with default parameters and same name. 这导致Field验证器具有两个具有默认参数和相同名称的通用方法。

Here is a shorter example that behaves in roughly the same way: 这是一个较短的例子,行为方式大致相同:

def foo[A](f: A => Boolean, x: Int = 0): Unit =  {}
def foo[A](f: A => Boolean, x: String): Unit =  {}

foo[String](_.isEmpty)

It results in: 它导致:

error: missing parameter type for expanded function ((x$1: ) => x$1.isEmpty) 错误:扩展函数缺少参数类型((x $ 1:)=> x $ 1.isEmpty)

  foo[String](_.isEmpty) ^ 

I can't pinpoint what exactly goes wrong, but essentially, you have confused the compiler with too much ambiguity by throwing three different sorts of polymorphism at it: 我无法确定究竟出现了什么问题,但实质上,您通过抛出三种不同类型的多态来使编译器混淆太多歧义:

  • Overloading: you have two methods with name apply 重载:您有两个名称apply方法
  • Generics: your methods have a generic type parameter [A] 泛型:你的方法有一个泛型类型参数[A]
  • Default arguments: your errorMessage ( x in my shorter example) can be omitted. 默认参数:您的errorMessage (在我的简短示例中为x )可以省略。

Together, this leaves the compiler with the choice between two equally named methods with unclear types and unclear number of expected type arguments. 总之,这使得编译器可以在两个同名的方法之间进行选择,这些方法具有不清楚的类型和预期类型参数的数量不清楚。 While flexibility is good, too much flexibility is simply too much , and the compiler gives up trying to figure out what you wanted, and forces you to specify all types of every single argument explicitly, without relying on inference. 虽然灵活性很好, 但是太多的灵活性太多了 ,编译器放弃尝试找出你想要的东西,并强制你明确指定所有类型的每一个参数,而不依赖于推理。

Theoretically, it could have figured it out in this particular case, but this would require much more complex inference algorithms and much more backtracking and trial-and-error (which would slow down the compilation in the general case). 从理论上讲,它可以在这种特殊情况下找到它,但这需要更复杂的推理算法和更多的回溯和反复试验(这会在一般情况下减慢编译速度)。 You don't want the compiler to spend half a day playing typesystem-sudoku, even if it theoretically could figure out a unique solution. 你不希望编译器花半天时间玩类型系统-dudoku,即使它理论上可以找出一个独特的解决方案。 Exiting quickly with an error message is a reasonable alternative. 快速退出并显示错误消息是一种合理的替代方法。


Workaround 解决方法

As a simple work-around, consider reordering the arguments in a way that allows the compiler to eliminate ambiguity as fast as possible. 作为一种简单的解决方法,请考虑以允许编译器尽可能快地消除歧义的方式重新排序参数。 For example, splitting the arguments into two argument lists, with two String s coming first, would make it unambiguous: 例如,将参数拆分为两个参数列表,首先使用两个String ,这将使其明确:

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM