user79074
user79074

Reputation: 5300

Function reference as implicit parameter with default value not as expected

Say I define he following function with an implicit default parameter as follows:

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

This works as expected until I pass an option as a parameterized type:

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

Gives me the following output:

Got value: Text

Got value: 3

Got value: Some(Text)

Validating: Some(Text)

I am a bit baffled as to how the equality fails when I pass an Option[String] to my function. Why is the isValid comparison failing and what validate function could it be calling?

Upvotes: 2

Views: 111

Answers (3)

Mike Allen
Mike Allen

Reputation: 8299

Implicit arguments are identified by their signatures, and in this case, that signature is T => Option[String]. In the scope of call to input, the compiler is picking up an Option[String] => Option[String] function (as other posters have pointed out).

If you want to change that behavior, you might want to consider creating a new type for the implicit value. Something like this would work:

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

You would then declare input as follows (comparing to a default value for a generic method is troublesome):

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

You can now provide validators for different types of T. Here's a default Option[String] validator:

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

Note that you might have to explicitly state the type when calling input (since it would infer T to be a Some[String], rather than an Option[String]):

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

or, more briefly:

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

The default implicit values (type erasure permitting) would go into the same scope as input (such as in the same class/object/trait/package object), and you can override them in code calling input.

Upvotes: 3

Cyrille Corpet
Cyrille Corpet

Reputation: 5305

In the third case, there is an implicit Option[String] => Option[String] in scope, which is the identity.

In fact, if A <: B, the function (x: A) => x: B is always in the scope.

It is somehow complicated to compare check that two functions are equals. Mathematically speaking, this can only be checked on all possible values, but this is not computationaly feasible for most types (you could it for Boolean or Unit).

Upvotes: 0

Jasper-M
Jasper-M

Reputation: 15086

The Standard library provides the A =:= B evidence for proving that types A and B are equal. A =:= B is a subtype of A => B. So when you ask for an implicit Option[String] => Option[String] scalac finds an implicit instance of Option[String] =:= Option[String].

Upvotes: 3

Related Questions