TypeTest 以克服 scala3 中泛型类型的擦除

问题描述

我在理解 scala3 中的 //@ts-check /// <reference types="c:/Users/USERNAME/.vscode/extensions/ego-digital.vscode-powertools-0.64.0/node_modules/vscode" /> // Allows us to reference the `vscode` module with jsdoc `@type` async function vscodeⁱ() {if (1 == 1) return null; return import ('vscode')} exports.execute = async (args) => { // Allows us to reference the `vscode` module with jsdoc `@type` const vscode = await vscodeⁱ() /** @type {vscode} */ const vs = args.require ('vscode') // NB: The following code is fully typed in VSCode const windowᵛ = vs.window const editorᵛ = windowᵛ.activeTextEditor const start = editorᵛ.selection.start } 如何替代 scala 2 中的 TypeTest 时遇到问题。用例能够匹配 x: List[Int] 等通用参数。

我试图解决的具体例子:

TypeTag

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

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] => ??? 一个无法访问的案例。

现在我的问题是:这在 Scala3 中完全可行吗?

我的印象是我必须以某种方式为所有 X 提供一个 the type test for Mod[Foo.Bar] cannot be checked at runtime,这些 X 是 TypeTest[Any,Mod[Foo.X]] 枚举的子类。

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

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

modifyBarsOrBaz

以及一个简单的 tt 实现

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

我试图在网上搜索答案,但由于这是一个全新的内容,我并不走运。有什么提示吗?

解决方法

这里的问题是 1: Task failed with an exception. ----------- * What went wrong: Execution failed for task ':app:mergeExtDexDebug'. > Could not resolve all files for configuration ':app:debugRuntimeClasspath'. > Failed to transform weka-stable-3.8.5.jar (nz.ac.waikato.cms.weka:weka-stable:3.8.5) to match attributes {artifactType=android-dex,dexing-enable-desugaring=true,dexing-incremental-transform=false,dexing-is-debuggable=true,dexing-min-sdk=16,org.gradle.category=library,org.gradle.libraryelements=jar,org.gradle.status=release,org.gradle.usage=java-runtime}. > No variants of com.github.fommil.netlib:all:1.1.2 match the consumer attributes: - com.github.fommil.netlib:all:1.1.2: - Incompatible because this component declares attribute 'artifactType' with value 'pom' and the consumer needed attribute 'artifactType' with value 'android-classes-jar' - Other compatible attributes: - Doesn't say anything about dexing-enable-desugaring (required 'true') - Doesn't say anything about dexing-incremental-transform (required 'false') - Doesn't say anything about dexing-is-debuggable (required 'true') - Doesn't say anything about dexing-min-sdk (required '16') > Failed to transform mtj-1.0.4.jar (com.googlecode.matrix-toolkits-java:mtj:1.0.4) to match attributes {artifactType=android-dex,org.gradle.usage=java-runtime}. > No variants of com.github.fommil.netlib:all:1.1.2 match the consumer attributes: - com.github.fommil.netlib:all:1.1.2: - Incompatible because this component declares attribute 'artifactType' with value 'pom' and the consumer needed attribute 'artifactType' with value 'android-classes-jar' - Other compatible attributes: - Doesn't say anything about dexing-enable-desugaring (required 'true') - Doesn't say anything about dexing-incremental-transform (required 'false') - Doesn't say anything about dexing-is-debuggable (required 'true') - Doesn't say anything about dexing-min-sdk (required '16') * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. 将能够检查 TypeTest[Any,Mod[T]] 是否是 Any,而不是检查 Mod[T] 是否是 {{ 1}} 或 Mod[T]。您需要的是 Mod[Foo.Bar]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]。由于 JVM 没有具体化,因此您必须在 TypeTest[Any,Mod[Foo.Bar]] 类中存储有关 TypeTest 的信息。如果你真的想保留 T 一个单一的案例类,你可以这样做:

Mod
Modtype 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],asBaz: TypeTest[Mod[Foo],Mod[Foo.Baz]]) = mod match
  case asBar(barMod) => println("barMod")
  case asBaz(bazMod) => println("bazMod")

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

apply

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