PartialFunction orElse在其类型边界上是否比它应该更宽松?

时间:2014-08-19 23:25:37

标签: scala types partialfunction

我们定义一个PartialFunction[String, String]和一个PartialFunction[Any, String]

现在,给定orElse

的定义
def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] 

因为

,我希望不能把这两者组合起来

AString
A1Any

因此绑定的A1 <: A(即Any <: String)不成立。

出乎意料的是,我可以撰写它们并获得在整个PartialFunction[String, String]域中定义的String。这是一个例子:

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

此外,如果我明确提供orElse类型参数

a orElse[Any, String] b
// error: type arguments [Any,String] do not conform to method orElse's type parameter bounds [A1 <: String,B1 >: String]

编译器最终显示出一些意义。

我是否缺少任何类型的系统法术,导致b成为orElse的有效参数?换句话说,A1如何推断为String

如果编译器从A1推断b,那么它必须是Any,那么导致String的推理链在哪里开始?


更新

在使用REPL后,我注意到orElse在类型不匹配时返回交集类型A with A1。示例:

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>

由于(String with Int) <:< String这是有效的,即使生成的函数几乎无法使用。我还怀疑String with Any统一到Any,鉴于

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

这就是为什么将StringAny结果混合到String中。

话虽如此,引擎盖下发生了什么?统一不匹配类型的逻辑是什么?

更新2

我已将问题简化为更一般的形式:

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's type parameter bounds [A <: String]

4 个答案:

答案 0 :(得分:4)

我打算说PartialFunction[Any, String]PartialFunction[String, String]的子类型,因为如果我理解正确,则会出现反方差。这可以解释在更新之前你的问题中描述的行为,但是你让我把这种联合类型的东西搞砸了。

我甚至不知道地狱String with Int意味着什么!

答案 1 :(得分:4)

你正在颠覆这一点。

您始终可以传递给需要A类型参数的任何类型为B <: A的参数的方法,即A的任何子类型。那就是你有

def foo(a: Animal)

您可以将Dog传递给foo,因为Dog <: Animal

以同样的方式,如果你有

def foo(l: List[Animal])

您可以向List[Dog]传递List,因为Dog <: Animal与其类型参数是协变的,因为List[Dog] <: List[Animal],然后是def foo(pf: PartialFunction[String, String])

现在,如果你有

PartialFunction[Any, String]

您可以传递PartialFunction,因为Any >: String与第一个类型参数相反,而与第二个类型参数协变。自PartialFuncion[Any, String] <: PartialFunction[String, String]起,然后A1

现在,对于类型边界,编译器将尝试推断B1A1,这样

  • AB2
  • 的子类型
  • BAny
  • 的子类型

为此,它将寻找:

  • StringA的最常见子类型,因为A1String处于逆变位置
  • StringB的最不常见的超类型,因为B1A1是协变位置

结果

  • StringB1
  • StringPartialFunction[String, String

使用PartialFunction[Int, Int]撰写String]的情况与前一个示例相比有些奇怪,其中:

  • IntString with Int的最大公共子类型是Nothing,即两种类型的联合部分,它们都是两者的子类型(在这种情况下,几乎就是说{{} 1}}: 一个String和一个Int似乎不太可能)
  • StringInt的最不常见的超类型是Any

因此

val a: PartialFunction[String, String] = ...
val b: PartialFunction[Int, Int] = ...
a orElse b // PartialFunction[String with Int, Any] // as expected, although not very useful...

答案 2 :(得分:1)

这当然含糊不清,只有我的拙见。建议和意见表示赞赏。

从这个问题中解脱出来。 (How to know if an object is an instance of a TypeTag's type?

import scala.reflect.runtime.universe._
implicit class MyInstanceOf[U: TypeTag](that: U) {
  def myIsInstanceOf[T: TypeTag] = 
    typeOf[U] <:< typeOf[T]
}

我们有一种正确的方法来检查isInstanceOf而不会删除。

val b: PartialFunction[Any, String] = { case _ => "default" }
b.myIsInstanceOf[PartialFunction[String, String]] //true

它才有意义。如果你有Any => String的函数,那么它接受任何输入。所以它也接受String输入。这就是为什么它也可以被视为来自String => String的函数。对于任何T => String,基本上可以将其视为T

最后编译器同意A -> StringA1 -> String

 a.orElse[String,String](b) //works

编辑:最后的想法

您不应该将A1 <: A视为限制。它只会推断出结果PartialFunction的类型。 无法应用orElse。所涉及的PF都是A的反变量,因此可以始终找到满足A1 <: A的BOTH的共同子类型。

我认为类比会增加分数,你认为,哦,它们没有共同点,因此无法添加。可以调整两个分数(或者:以不同方式看到)以具有共同的分母。编译器虽然希望找到最小的公分母,而不是采用与其他分母相乘的简单方法。 (A with A'

对于其他类型B,它是相同的。两者都是共变体,因此也总能找到共同的超类型。最糟糕的情况是Any

答案 3 :(得分:1)

您已输入此内容,但是:

scala> val a: PartialFunction[String, String] = { case "a" => "b" }
a: PartialFunction[String,String] = <function1>

scala> val b: PartialFunction[Any, String] = { case 1 => "one" }
b: PartialFunction[Any,String] = <function1>

scala> a orElse b
res0: PartialFunction[String,String] = <function1>

scala> a orElse[String,String] b
res1: PartialFunction[String,String] = <function1>

scala> a orElse[Any,String] b
<console>:10: error: type arguments [Any,String] do not conform to method orElse's type parameter bounds [A1 <: String,B1 >: String]
              a orElse[Any,String] b
              ^

scala> import reflect.runtime._ ; import universe._
import reflect.runtime._
import universe._

scala> typeOf[PartialFunction[Any,String]] <:< typeOf[PartialFunction[String,String]]
res3: Boolean = true

由于逆变型param,你可以在这里使用PF [Any,String]。

要回答这个问题,它会说出它会选择什么?

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#local-type-inference

例如,它承诺在逆变位置推断出最大A1,但仍然符合约束&lt ;: A。