函数引用为隐式参数,默认值不符合预期

时间:2017-04-28 12:38:20

标签: scala

假设我使用隐式默认参数定义了以下函数,如下所示:

val isValid: Any => Option[String] = _ => None

def input[T](f: () => T)(implicit validate: T => Option[String] = isValid) = {
    val t = f()
    println("Got value: " + t)
    if (validate != isValid) {
        println("Validating: " + validate(t))
    }
}

这可以按预期工作,直到我将选项作为参数化类型传递:

    input(() => "Text")
    input(() => 3)
    input(() => Some("Text"))

给我以下输出:

  

有价值:文字

     

有价值:3

     

有价值:有些(文字)

     

验证:部分(文字)

当我将Option [String]传递给我的函数时,我对有关如何失败感到困惑。为什么isValid比较失败以及它可以调用哪个验证函数?

3 个答案:

答案 0 :(得分:3)

标准库提供A =:= B证据,证明AB类型相同。 A =:= BA => B的子类型。因此,当您要求隐式Option[String] => Option[String] scalac找到Option[String] =:= Option[String]的隐式实例时。

答案 1 :(得分:3)

隐式参数由其签名标识,在这种情况下,该签名为T => Option[String]。在调用input的范围内,编译器正在获取Option[String] => Option[String]函数(正如其他海报所指出的那样)。

如果要更改该行为,可能需要考虑为隐式值创建新类型。像这样的东西会起作用:

case class Validator[T](v: T => Option[String])

然后您将如下声明input(与通用方法的默认值相比较麻烦):

def input[T](f: () => T)(implicit validate: Validator[T]) = {
  val t = f()
  println(s"Got value: $t")
  val result = validate.v(t)
  println(s"Got validation result: $result")
}

您现在可以为不同类型的T提供验证器。这是默认的Option[String]验证器:

implicit val defaultIsValidOptionString = Validator[Option[String]](_ => None)

请注意,您可能必须在调用输入时显式声明类型(因为它会将T推断为Some[String],而不是Option[String]):

input[Option[String]](() => Some("Text"))

或者更简单地说:

input(() => Option("Text"))

默认隐式值(类型擦除允许)将与input进入相同的范围(例如在相同的类/对象/特征/包对象中),并且您可以在调用input的代码中覆盖它们。

答案 2 :(得分:0)

在第三种情况下,范围中存在隐式Option[String] => Option[String],即身份。

事实上,如果A <: B,函数(x: A) => x: B始终在范围内。

比较检查两个函数是否相等有点复杂。从数学上讲,这只能在所有可能的值上进行检查,但对于大多数类型而言,这不是计算可行的(您可以将其用于BooleanUnit)。