类型稳定参数多态性
type stable parametric polymorphism
我不明白为什么以下 Scala 代码无法编译:
sealed trait A
case class B() extends A {
def funcB: B = this
}
case class C() extends A {
def funcC: C = this
}
def f[T <: A](s:T): T = s match {
case s: B => s.funcB
case s: C => s.funcC
}
用
替换f
有效
def f[T <: A](s:T): A = s match {
case s: B => s.funcB
case s: C => s.funcC
}
然后在调用 f
时转换为子类型,例如使用 asInstanceOf
。但是我希望能够构造一个函数来统一一些以前定义的方法,并使它们类型稳定。谁能解释一下?
此外,请注意以下 f
也会编译:
def f[T <: A](s:T): T = s match {
case s: B => s
case s: C => s
}
它的工作原理是什么?
特别是在 Scala 3 中,您可以使用匹配类型
scala> type Foo[T <: A] = T match {
| case B => B
| case C => C
| }
|
| def f[T <: A](s:T): Foo[T] = s match {
| case s: B => s.funcB
| case s: C => s.funcC
| }
def f[T <: A](s: T): Foo[T]
scala> f(B())
val res0: B = B()
scala> f(C())
val res1: C = C()
一般来说,对于“return 当前类型”问题的解决方案,请参阅 Scala FAQ How can a method in a superclass return a value of the “current” type?
类型 类 和匹配类型等编译时技术可以被视为一种编译时模式匹配,它指示编译器减少到在调用站点使用的最具体的信息丰富的类型,而不是否则必须确定可能较差的上限类型。
为什么不起作用?
要理解的关键概念是参数多态性是一种通用量化,这意味着它必须对编译器有意义对于调用站点类型参数的所有 实例化。考虑打字规范
def f[T <: A](s: T): T
编译器可能会这样解释
For all types T
that are a subtype of A
, then f
should return that
particular subtype T
.
因此表达式 expr
表示 f
的正文
def f[T <: A](s:T): T = expr
必须键入特定的 T
。现在让我们尝试输入 expr
s match {
case s: B => s.funcB
case s: C => s.funcC
}
类型
case s: B => s.funcB
是B
,
的类型
case s: C => s.funcC
是C
。鉴于我们有 B
和 C
,现在编译器必须取两者中的最小上限,即 A
。但是 A
肯定不总是 T
。因此类型检查失败。
现在让我们用
做同样的练习
def f[T <: A](s: T): A
这个规范的意思是(再观察一下“for all”)
For all types T
that are a subtype of A
, then f
should return
their supertype A
.
现在让我们输入方法主体表达式
s match {
case s: B => s.funcB
case s: C => s.funcC
}
在我们到达类型 B
和 C
之前,编译器采用上限,即超类型 A
。事实上,这正是我们指定的 return 类型。所以类型检查成功了。然而,尽管成功了,但在编译时我们丢失了一些类型信息,因为编译器将不再考虑在调用站点传入的特定 T
附带的所有信息,而只考虑通过其超类型 A
提供的信息.例如,如果T
有一个成员不存在于A
,那么我们将无法调用它。
要避免什么?
关于asInstanceOf
,这是我们告诉编译器停止帮助我们,因为我们会下雨。两组人倾向于在 Scala 中使用它来使事情正常进行, 库作者和那些从其他更动态类型的语言转换过来的人。然而,在大多数应用程序级代码中,这被认为是不好的做法。
回答问题为什么它不起作用。
f
returns 语句的结果 s match {...}
.
该语句的类型是A
(有时是returnsB
,有时是returnsC
),不是 T
,因为它应该是。 T
有时是 C
,有时 B
,s match {...}
是 从不 中的任何一个。它是它们的超类型,即 A
.
回复。这个:
s match {
case s: B => s
case s: C => s
}
这条语句的类型显然是T
,因为s
是T
。它肯定 does compile 尽管 @jwvh 可能会说:)
这一切都归结于我们的老朋友(恶魔?)compile-time/run-time 障碍。 (以后再也不会见面了。)
T
在调用站点的编译时解析。当编译器看到 f(B)
时,T
表示 B
,当编译器看到 f(C)
时,T
变为 C
。
但是 match { case ...
在 运行 时解决了。编译器不知道将选择哪个 case
分支。从编译器的角度来看,所有 case
选项都是同样可能的。因此,如果 T
解析为 B
,但代码 可能 采取 C
分支......好吧,编译器不允许这样做。
查看做什么编译:
def f[T <: A](s:T): A = s match { //f() returns an A
case s: B => s.funcB //B is an A sub-type
case s: C => s.funcC //C is an A sub-type
} //OK, all is good
你的第二个“同样有效”的例子不适合我。
我不明白为什么以下 Scala 代码无法编译:
sealed trait A
case class B() extends A {
def funcB: B = this
}
case class C() extends A {
def funcC: C = this
}
def f[T <: A](s:T): T = s match {
case s: B => s.funcB
case s: C => s.funcC
}
用
替换f
有效
def f[T <: A](s:T): A = s match {
case s: B => s.funcB
case s: C => s.funcC
}
然后在调用 f
时转换为子类型,例如使用 asInstanceOf
。但是我希望能够构造一个函数来统一一些以前定义的方法,并使它们类型稳定。谁能解释一下?
此外,请注意以下 f
也会编译:
def f[T <: A](s:T): T = s match {
case s: B => s
case s: C => s
}
它的工作原理是什么?
特别是在 Scala 3 中,您可以使用匹配类型
scala> type Foo[T <: A] = T match {
| case B => B
| case C => C
| }
|
| def f[T <: A](s:T): Foo[T] = s match {
| case s: B => s.funcB
| case s: C => s.funcC
| }
def f[T <: A](s: T): Foo[T]
scala> f(B())
val res0: B = B()
scala> f(C())
val res1: C = C()
一般来说,对于“return 当前类型”问题的解决方案,请参阅 Scala FAQ How can a method in a superclass return a value of the “current” type?
类型 类 和匹配类型等编译时技术可以被视为一种编译时模式匹配,它指示编译器减少到在调用站点使用的最具体的信息丰富的类型,而不是否则必须确定可能较差的上限类型。
为什么不起作用?
要理解的关键概念是参数多态性是一种通用量化,这意味着它必须对编译器有意义对于调用站点类型参数的所有 实例化。考虑打字规范
def f[T <: A](s: T): T
编译器可能会这样解释
For all types
T
that are a subtype ofA
, thenf
should return that particular subtypeT
.
因此表达式 expr
表示 f
def f[T <: A](s:T): T = expr
必须键入特定的 T
。现在让我们尝试输入 expr
s match {
case s: B => s.funcB
case s: C => s.funcC
}
类型
case s: B => s.funcB
是B
,
case s: C => s.funcC
是C
。鉴于我们有 B
和 C
,现在编译器必须取两者中的最小上限,即 A
。但是 A
肯定不总是 T
。因此类型检查失败。
现在让我们用
做同样的练习def f[T <: A](s: T): A
这个规范的意思是(再观察一下“for all”)
For all types
T
that are a subtype ofA
, thenf
should return their supertypeA
.
现在让我们输入方法主体表达式
s match {
case s: B => s.funcB
case s: C => s.funcC
}
在我们到达类型 B
和 C
之前,编译器采用上限,即超类型 A
。事实上,这正是我们指定的 return 类型。所以类型检查成功了。然而,尽管成功了,但在编译时我们丢失了一些类型信息,因为编译器将不再考虑在调用站点传入的特定 T
附带的所有信息,而只考虑通过其超类型 A
提供的信息.例如,如果T
有一个成员不存在于A
,那么我们将无法调用它。
要避免什么?
关于asInstanceOf
,这是我们告诉编译器停止帮助我们,因为我们会下雨。两组人倾向于在 Scala 中使用它来使事情正常进行,
回答问题为什么它不起作用。
f
returns 语句的结果 s match {...}
.
该语句的类型是A
(有时是returnsB
,有时是returnsC
),不是 T
,因为它应该是。 T
有时是 C
,有时 B
,s match {...}
是 从不 中的任何一个。它是它们的超类型,即 A
.
回复。这个:
s match {
case s: B => s
case s: C => s
}
这条语句的类型显然是T
,因为s
是T
。它肯定 does compile 尽管 @jwvh 可能会说:)
这一切都归结于我们的老朋友(恶魔?)compile-time/run-time 障碍。 (以后再也不会见面了。)
T
在调用站点的编译时解析。当编译器看到 f(B)
时,T
表示 B
,当编译器看到 f(C)
时,T
变为 C
。
但是 match { case ...
在 运行 时解决了。编译器不知道将选择哪个 case
分支。从编译器的角度来看,所有 case
选项都是同样可能的。因此,如果 T
解析为 B
,但代码 可能 采取 C
分支......好吧,编译器不允许这样做。
查看做什么编译:
def f[T <: A](s:T): A = s match { //f() returns an A
case s: B => s.funcB //B is an A sub-type
case s: C => s.funcC //C is an A sub-type
} //OK, all is good
你的第二个“同样有效”的例子不适合我。