type constraints and reifications regarding to joi

2019-06-09 16:03发布

问题:

joinLeft is defined as:

abstract class Either[+A, +B]

def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]):
    Either[C, B1] = this match {
    case Left(a)  => a
    case Right(b) => Right(b)
}
  • With the known A and B, we need an implicit ev: A1 <:< Either[C, B1] that
    1. satisfies constraint A1 >: A, B1 >: B.
    2. reifies A1 <: Either[C, B1]

  • For that we'll need implicit conforms[A1] and conforms[Either[C, B1]]

If I'm still right until now, there seem to me many choices for A1 and B1 as long as they are beyond lower bounds A and B. So I would like to know how scala gives us A1 and Either[C, B1](and what they are) so that we get implicit conforms to facilitate <:< to do its job of asserting A1 <: Either[C, B1].

P.S.
I think this question is somewhat related to my another one "joinLeft [A1 >: A, B1 >: B, C]… why are type constraint A1 >: A and B1>: B necessary?". I would appreciate if anyone can also take a look at it.

回答1:

You are right, there are many choices. In general, type inference on generic method uses the arguments to determine the value of the generic parameter

def myMethod[A](aList:List[A])

However, this is not the only way to use type inference of generic arguments. Notably, type parameters can be :

  • explicit
  • determined using the expected result type

In this case, as the generic type parameter cannot be determined from the arguments (because there are no explicit arguments), you typically assign the result of the method to a typed variable, or you use it at the end of a method with explicit return type.

You might ask yourself how A1 will be solved and this is a very interesting question. In fact A1 is not present in the input type, nor in the output type. What is it useful for?

The answer is in the following definition, coming from Predef.scala

@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
  sealed abstract class <:<[-From, +To] extends (From => To) with Serializable

Since the <:< is contravariant in From the method

def joinLeft[B1 >: B, C](implicit ev: A <:< Either[C, B1]):
    Either[C, B1] = this match {
    case Left(a)  => a
    case Right(b) => Right(b)
}

won't handle correctly subclassing on A. That's why you need an extra generic type parameter A1, which is resolved by the Scala compiler by using the A1 which gives the implicit with the highest priority.