Define a PartialFunction[String, String] and a PartialFunction[Any, String]
Now, given the definition of orElse
def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1]
I expect that I canβt put them together, because
A β String
A1 β Any
and therefore, A1 <: A (i.e., Any <: String ) is not evaluated.
Suddenly, I can compose them and get the PartialFunction[String, String] defined in the entire String domain. Here is an example:
val a: PartialFunction[String, String] = { case "someString" => "some other string" } // a: PartialFunction[String,String] = <function1> val b: PartialFunction[Any, String] = { case _ => "default" } // b: PartialFunction[Any,String] = <function1> val c = a orElse b // c: PartialFunction[String,String] = <function1> c("someString") // res4: String = some other string c("foo") // res5: String = default c(42) // error: type mismatch; // found : Int(42) // required: String
Also, if I explicitly provide parameters of type orElse
a orElse[Any, String] b // error: type arguments [Any,String] do not conform to method orElse type parameter bounds [A1 <: String,B1 >: String]
the compiler finally makes some sense.
Is there any systemic witchcraft that I skip that results in b being a valid argument for orElse ? In other words, how is it that A1 output as String ?
If the compiler outputs A1 from b , then it should be Any , so where does the output chain leading to String begin?
Update
After playing with REPL, I noticed that orElse returns the intersection type A with A1 if the types do not match. Example:
val a: PartialFunction[String, String] = { case "someString" => "some other string" } // a: PartialFunction[String,String] = <function1> val b: PartialFunction[Int, Int] = { case 42 => 32 } // b: PartialFunction[Int,Int] = <function1> a orElse b // res0: PartialFunction[String with Int, Any] = <function1>
Since (String with Int) <:< String this works, although the resulting function is almost unusable. I also suspect that String with Any unified in Any , given that
import reflect.runtime.universe._ // import reflect.runtime.universe._ typeOf[String] <:< typeOf[String with Any] // res1: Boolean = true typeOf[String with Any] <:< typeOf[String] // res2: Boolean = true
So why mixing String and Any results in String .
As the saying goes, what happens under the hood? Under what logic are inappropriate types combined?
Update 2
I reduced the problem to a more general form:
class Foo[-A] { def foo[B <: A](f: Foo[B]): Foo[B] = f } val a = new Foo[Any] val b = new Foo[String] a.foo(b) // Foo[String] Ok, String <:< Any b.foo(a) // Foo[String] Shouldn't compile! Any <:!< String b.foo[Any](a) // error: type arguments [Any] do not conform to method foo type parameter bounds [A <: String]