在 Scala 3 中是否可以通过泛型进行模式匹配?

Is it possible in Scala 3 to do pattern matching by generic type?

我正在尝试将我的想法从 Java 背景(和一点 Scala 2)迁移到 Scala 3 并实现一种 PartialFunction.orElse 的版本,但联合类型作为输入参数而不是交集类型:

trait F[A, B] extends (A => B) {
  
  def orElse[A1, B1](other: F[A1, B1]): F[A | A1, B | B1] = {
    val self = this
    
    new F[A | A1, B | B1] {
      override def apply(v: A | A1): B | B1 = {
        helper(v)
      }
    
      transparent inline def helper(v: A | A1): Any = {
        inline v match {
          case _: A => self.apply(v.asInstanceOf[A])
          case _: A1 => other.apply(v.asInstanceOf[A1])
        }
      }
    }
  }
}

但我最终发现两个调用都转到了“匹配”的第一个分支:

 val f1 = new F[Int, String] {
    override def apply(v: Int): String = s"Int => String : $v"
  }

  val f2 = new F[String, Int] {
    override def apply(v: String): Int = v.length
  }

  val f = f1.orElse(f2)

println(f(42)) // prints "Int => String : 42"
println(f("hello")) // fails in runtime because of trying casting String to Int

所以,我的问题是,这可能吗?如果是,我错过了什么?

helper 可能被内联,但整个特征都没有,orElse 也没有,所以 AA1 仍然被删除。您应该从编译器那里得到一条警告,指出 AA1 无法在运行时检查。为此,您需要在范围内有一个 Typeable[A] 和一个 Typeable[A1]TypeTest[A | A1, A] 和一个 TypeTest[A | A1, A1] 也应该有效,因为它们更具体,但是出于某种原因,编译器不使用它们)。

我决定使用扩展方法而不是特征,因为这样更简单。这样,您也可以将它用于普通功能,只要它们在范围内有一个 Typeable 实例(我相信 IntString 和其他 primitives/classes 的实例没有类型参数或自动合成成员)。 (Scastie)

import scala.reflect.Typeable

extension [A1: Typeable, B1](f1: A1 => B1)
  def orElse[A2: Typeable, B2](f2: A2 => B2): (A1 | A2) => (B1 | B2) =
    case v1: A1 => f1(v1)
    case v2: A2 => f2(v2)

val f1 = (v: Int) => s"Int => String : $v"
val f2 = (v: String) => v.length
val f = f1.orElse(f2)

但是,对我来说,显式检查类型感觉像是一种反模式,因此,如果您能提供有关更大问题的更多详细信息,将会有所帮助。