在Scala 3中,是否可以根据精化类型调用隐式?

g52tjvyc  于 2023-08-05  发布在  Scala
关注(0)|答案(1)|浏览(113)

考虑以下示例:

{
      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,编译将成功。这里缺少了什么?

jtw3ybtb

jtw3ybtb1#

我看到你正在使用Aux模式的老技巧来表达某种含义:我有两个参数,但如果其中一个是X,那么另一个应该是Y。
事情是,从经验来看:如果满足以下条件,则效果最佳:

  • 用于“获取匹配”的类型是普通类型参数-不是依赖于路径的类型!
  • 应该“被隐含”的类型应该显示为路径相关类型
  • 那么为了方便起见,可以使用Aux将两个组表示为类型参数

一旦我们以这种方式重写了你的代码,它就会工作:

trait Gen[S] {
  type Ext
}
object Gen extends Gen_Fallback0 {
  type Aux[S, _Ext] = Gen[S] { type Ext = _Ext }
}
trait Gen_Fallback0 extends Gen_Fallback1 { this: Gen.type =>
  given Gen.Aux[Int, Long] = new Gen[Int] {
    final override type Ext = Long
  }
}
trait Gen_Fallback1 { this: Gen.type =>
  given [S]: Gen.Aux[S, S] = new Gen[S] {
    final override type Ext = S
  }
}

val gen = summon[Gen[Int]]
summon[gen.Ext =:= Long]

字符串
(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 }

相关问题