TypeTest для преодоления стирания с помощью универсальных типов в scala3
Я не понимаю, как
TypeTest
s в scala3 может заменить использование
TypeTag
в scala 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?
Я попытался поискать ответы в Интернете, но так как это довольно ново, мне не повезло. Какие-нибудь намеки?
1 ответ
Проблема здесь в том, что
TypeTest[Any, Mod[T]]
сможет проверить, есть ли
Any
не проверять, действительно ли
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]
не работал с
TypeTest[Any, Mod[Foo.Bar]]
по объему, я вернусь к этому позже.
Однако теперь вам нужно реализовать их самостоятельно. Поскольку у JVM нет реификации, вам придется хранить информацию о
T
в классе. Если ты действительно хочешь сохранить
Mod
один класс case, вы можете сделать это:
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]])
И ты мог бы использовать это так (Скэсти ):
modifyBarsOrBaz(Mod[Foo.Bar](bar => bar)) //barMod
modifyBarsOrBaz(Mod[Foo.Baz](baz => baz)) //bazMod
Но на самом деле необходимость в типовых тестах, подобных этому, кажется мне запахом кода. Я бы посоветовал переосмыслить ваш дизайн, чтобы обойти это, а не использовать подобные теги.