1

我在理解TypeTestscala3 中的 s 如何替换TypeTagscala 2 中的使用时遇到问题。用例能够匹配诸如 x: List [Int] 之类的通用参数。

我试图解决的具体例子:

enum Foo :
  case Bar()
  case Baz()

case class Mod[T <: Foo](modFn: T => T)

def modifyBarsOrBaz(mod: Mod[_]) = mod match
  case barMod: Mod[Foo.Bar] => ???
  case bazMod: Mod[Foo.Baz] => ???

编译器警告中的编译结果(如预期)

the type test for Mod[Foo.Bar] cannot be checked at runtime和一个遥不可及的案例。

现在我的问题是:这可能在 scala3 中完成吗?

我的印象是我必须以某种方式TypeTest[Any, Mod[Foo.X]]为所有 X提供一个Foo枚举的子类。

但我什至在努力实现这些测试,以及了解它需要什么using参数modifyBarsOrBaz才能工作。

因此,我想出了以下(不工作)解决方案:

def modifyBarsOrBaz[T <: Foo](mod: Mod[T])(using TypeTest[Any, Mod[T]]) = mod match
  case barMod: Mod[Foo.Bar] => ???
  case bazMod: Mod[Foo.Baz] => ???

和一个天真的 tt 实现

val tt: TypeTest[Any, Mod[Foo.Bar]] =
  new TypeTest[Any, Mod[Foo.Bar]] :
    def unapply(x: Any): Option[x.type & Mod[Foo.Bar]] = x match
      case m: Mod[_] => ??? // what to do here? use a classtag on Mod?

我试图在网上搜索答案,但由于这是相当新的,我并不幸运。有什么提示吗?

4

1 回答 1

3

这里的问题是 aTypeTest[Any, Mod[T]]将能够检查 anAny是否是 a Mod[T],而不是检查 aMod[T]是 aMod[Foo.Bar]还是 a Mod[Foo.Baz]。你需要的是TypeTest[Any, Mod[Foo.Bar]TypeTest[Any, Mod[Foo.Baz]

def modifyBarsOrBaz(mod: Mod[?])(using asBar: TypeTest[Any, Mod[Foo.Bar]], asBaz: TypeTest[Any, Mod[Foo.Baz]]) =
  mod match
    case asBar(barMod) => println("barMod")
    case asBaz(bazMod) => println("bazMod")

我不知道为什么一个简单barMod: Mod[Foo.Bar]的在范围内不起作用TypeTest[Any, Mod[Foo.Bar]],我稍后会谈到。

但是,您现在必须自己实际实现这些TypeTests。由于 JVM 没有具体化,因此您必须TMod类中存储有关信息。如果你真的想保留Mod一个案例类,你可以这样做:

type BarOrBaz[T <: Foo] <: String = T match {
  case Foo.Bar => "Bar"
  case Foo.Baz => "Baz"
}

case class Mod[T <: Foo](modFn: T => T, tag: BarOrBaz[T])

TypeTest现在可以为Bar和的实例提供内联方法:Baz

import compiletime.constValue

inline given [T <: Foo]: TypeTest[Mod[?], Mod[T]] = new TypeTest:
  def unapply(mod: Mod[?]) = Option.when(mod.tag == constValue[BarOrBaz[T]])(mod.asInstanceOf[mod.type & Mod[T]])

modifyBarsOrBaz将会

def modifyBarsOrBaz(mod: Mod[?])(using asBar: TypeTest[Mod[Foo], Mod[Foo.Bar]], asBaz: TypeTest[Mod[Foo], Mod[Foo.Baz]]) = mod match
  case asBar(barMod) => println("barMod")
  case asBaz(bazMod) => println("bazMod")

apply为方便起见,可以制作一个内联方法:

object Mod:
  inline def apply[T <: Foo](modFn: T => T) = new Mod(modFn, constValue[BarOrBaz[T]])

你可以像这样使用它(Scastie):

modifyBarsOrBaz(Mod[Foo.Bar](bar => bar))  //barMod
modifyBarsOrBaz(Mod[Foo.Baz](baz => baz))  //bazMod

但实际上,对这样的类型测试的需求对我来说就像是代码味道。我建议重新考虑您的设计以解决它,而不是使用这样的标签。

于 2021-04-11T22:14:16.677 回答