提问人:tribbloid 提问时间:5/2/2023 更新时间:5/3/2023 访问量:102
scala 3 有真正的类型别名吗?如何实现?
Does scala 3 have true type alias? How to implement it?
问:
在许多 Scala 教程和营销材料中,我发现许多人通过混合“类型别名”和“依赖类型”来滥用术语,而实际上它们并不是一回事。
例如,在以下示例中,是依赖类型,而不是类型别名。因此,它会导致编译失败:TakeAlias
object TrueTypeAlias {
trait Unaliased[+T] {
def take1: List[Seq[(T, T)]]
def take2: List[Seq[(T, T)]]
def take3: List[Seq[(T, T)]]
}
trait Aliased[+T] {
type TakeAlias = List[Seq[(T, T)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
}
/*
TrueTypeAlias.scala:16:10: covariant type T occurs in invariant position in type = List[Seq[(T, T)]] of type TakeAlias
one error found
*/
问题是:实现真正的类型别名需要什么?是否有编译器机制/扩展可用于使其工作?
答:
1赞
tribbloid
5/2/2023
#1
找到了一个可能的解决方案:
object TrueTypeAlias {
trait Unaliased[+T] {
def take1: List[Seq[(T, T)]]
def take2: List[Seq[(T, T)]]
def take3: List[Seq[(T, T)]]
}
trait Aliased[+T] {
private type TakeAlias = List[Seq[(T, T)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
}
或者:
object TrueTypeAlias {
trait Unaliased[+T] {
def take1: List[Seq[(T, T)]]
def take2: List[Seq[(T, T)]]
def take3: List[Seq[(T, T)]]
}
trait Aliased[+T] {
opaque type TakeAlias = List[Seq[(T, T)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
trait Aliased2[+T] extends Aliased[T] {
def take4: TakeAlias
}
}
评论
0赞
Dmytro Mitin
5/2/2023
private
修复在 2.13 scastie.scala-lang.org/DmytroMitin/iTBIyhmdQNmZLD1QcBJJTg 中不起作用
1赞
Dmytro Mitin
5/2/2023
但工作 scastie.scala-lang.org/DmytroMitin/iTBIyhmdQNmZLD1QcBJJTg/1(如我回答中规范的引用所述)。 在 Scala 3 docs.scala-lang.org/scala3/reference/dropped-features/ 中不存在......private[this]
private[this]
1赞
Dmytro Mitin
5/2/2023
也许在 Scala 3 中,即使只对成员来说,方差检查也放宽了。我找不到特定的 PR、文档或问题。也许 github.com/lampepfl/dotty/issues/7567 是相关的private
2赞
Jasper-M
5/3/2023
@DmytroMitin Scala 3 会自动变成 如果成员只被使用 .您可以看到,如果添加 .private
private[this]
private[this]
def foo(a: Aliased[?]): a.TakeAlias
0赞
tribbloid
5/3/2023
非常感谢你们提供的新信息。对不起,刚刚意识到这不属于任何逻辑,因为方差是一个更高层次的概念。将移除相关部分
5赞
Dmytro Mitin
5/2/2023
#2
type TakeAlias = List[Seq[(T, T)]]
是可以代替 使用的类型别名。TakeAlias
List[Seq[(T, T)]]
同时是特征的类型成员。所以 (for ) 是依赖于路径的类型。虽然这种路径依赖关系现在是微不足道的:对于不同的是相同的,即都是。由于是类型成员,因此应用所有方差位置限制。type TakeAlias = ...
x.TakeAlias
x: Aliased[T]
x.TakeAlias
x: Aliased[T]
T
x.TakeAlias
List[Seq[(T, T)]]
type TakeAlias = ...
我会修复此代码,使类型别名通用
trait Aliased[+T] {
type TakeAlias[+S] = List[Seq[(S, S)]]
//type TakeAlias[S] = List[Seq[(S, S)]]
def take1: TakeAlias[T]
def take2: TakeAlias[T]
def take3: TakeAlias[T]
}
或提取外部的类型别名(例如,到 companion)
trait Aliased[+T] {
import Aliased.*
def take1: TakeAlias[T]
def take2: TakeAlias[T]
def take3: TakeAlias[T]
}
object Aliased {
type TakeAlias[+S] = List[Seq[(S, S)]]
//type TakeAlias[S] = List[Seq[(S, S)]]
}
规范中的一句话:
- 类型别名的右侧始终处于不变位置。
对类的对象私有值、类型、变量或方法中类型参数的引用不会检查其方差位置。在这些成员中,type 参数可以出现在任何位置,而不限制其合法方差注释。
此外,当您确定非法使用不会导致您的用例出现问题时,您可以使用scala.annotation.unchecked.uncheckedVariance
trait Aliased[+T] {
type TakeAlias = List[Seq[(T @uncheckedVariance, T @uncheckedVariance)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
或
trait Aliased[+T] {
type T1 = T @uncheckedVariance
type TakeAlias = List[Seq[(T1, T1)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
评论
type
TakeAlias
type-alias member
Aliased
type-alias
type
List[Seq[(T, T)]]
Int value member
i
10
type-alias
trait
type-alias
path dependent type
trait instance