考虑以下示例:
{
trait Gen {
type S
type Ext
}
type GenAux[_S] = Gen { type S = _S }
trait Gen_Fallback1 {
given [_S]: GenAux[_S] = new Gen {
final override type S = _S
final override type Ext = _S
}
}
trait Gen_Fallback0 extends Gen_Fallback1 {
given GenAux[Int] = new Gen {
final override type S = Int
final override type Ext = Long
}
}
object Gen extends Gen_Fallback0 {
given GenAux[Float] = new Gen {
final override type S = Float
final override type Ext = Double
}
}
val gen = summon[GenAux[Int]]
summon[gen.Ext =:= Long]
}
字符串
编译器(Scala3.3.0)将给予以下错误:
summon[gen.Ext =:= Long]
^
Cannot prove that gen.Ext =:= Long.
型
有趣的是,如果GenAux
不是类型别名,而是实际的泛型trait,编译将成功。这里缺少了什么?
1条答案
按热度按时间jtw3ybtb1#
我看到你正在使用
Aux
模式的老技巧来表达某种含义:我有两个参数,但如果其中一个是X,那么另一个应该是Y。事情是,从经验来看:如果满足以下条件,则效果最佳:
Aux
将两个组表示为类型参数一旦我们以这种方式重写了你的代码,它就会工作:
字符串
(see:Scastie)。
你可以认为它的工作方式是寻找隐式返回
Gen[Int]
。有这样的隐式-一个返回Gen.Aux[Int, Long]
或Gen[Int] { type Ext = Long }
。这个类型被分配给gen
值,编译器知道gen.Ext
的确切类型。在你的代码编译器寻找
GenAux[Int]
和得到.GenAux[Int]
。在类型签名中没有反映type Ext
。也许当您用泛型替换类型别名时,您意外地创建了一个保留了Ext
上的类型信息的精炼类型。同时,当你使用alias时,你有GenAux[Int]
,它可能已经从Gen { type S = Int; type Ext = Long }
“向上转换”到Gen { type S = Int }
。