从任意类型联合中类型级别删除一种类型

Dyl*_*lan 7 scala type-level-computation scala-3

如果我在 Scala 3 中有一个任意类型联合,是否可以编写一个从联合中“删除”一种类型的方法?

与 类似shapeless.ops.coproduct.Remove,但适用于本机 Scala 3。

例如,如果我有一个代表一些不同错误的联合类型,并且我想编写一个从一种特定错误类型中恢复的函数,并将其余错误保留为新的联合类型。

val result: Either[Foo | Bar | Baz | Bang, Thing]
val otherResult: Either[Foo | Bar, OtherThing]

// pretend syntax
def recoverBar[X, A](error: Bar | ...X)(f: Bar => A): Either[X, A] = 
  error match {
    case e: Bar => Right(f(e))
    case otherError => Left(otherError)
  }

// example usage
val recoveredResult: Either[Foo | Baz | Bang, Option[Thing]] = result
  .map { Option.apply }
  .left.flatMap { recoverBar(_)(_ => None) }

val recoveredOther: Either[Foo, OtherThing] = otherResult
  .left.flatMap { recoverBar(_)(_ => OtherThing.default) }
Run Code Online (Sandbox Code Playgroud)

即某种类型级别的通用方法

[Foo | Bar | Baz | Bang] =>> [Foo | Baz | Bang]
[Foo | Bar] =>> [Foo]
[Bar] =>> [Nothing]
Run Code Online (Sandbox Code Playgroud)

ste*_*bot 2

你可以用TypeTest

class Remove[A]:
  def apply[B](aOrB: A | B)(using tt: TypeTest[A | B, A]): Either[A, B] =
    aOrB match
      case a: A => Left(a)
      case b    => Right(b.asInstanceOf[B])

def remove[A]: Remove[A] = Remove[A]

type Foo = Boolean | Int | String
val foo: Foo = "foo"

val noInt = remove[Int](foo)

// It inferred the correct type:
val inferred: Either[Int, Boolean | String] = noInt

// And we get the expected value: Right(foo)
println(noInt) 
Run Code Online (Sandbox Code Playgroud)

比赛无法推断出另一种情况一定是 a,这有点糟糕B,但我认为这是我们目前能做的最好的事情。