是否可以匹配 sum 类型的带引号类型?

发布于 2025-01-14 16:21:46 字数 5079 浏览 6 评论 0原文

我正在 Scala3 中进行类型类推导。 我的类型类是 BarOf[T] 扩展 BarT 预计是一个 ADT。 Bar 有一个 children 方法,返回 T 的子类型的 BarOf[X]

这里是 衍生 方法基于Quotes而不是Mirror。这是我的其余代码所必需的,已在此处删除。

找出 BarOf[X](对于每个 X 子类型)的基本步骤是基于引用类型匹配。 (完整代码在文章末尾)

A.memberType(childSymbol).asType match {
        case '[f] => Expr.summon[BarOf[f]]
      }

这对于产品类型,特别是案例类来说效果很好。 但是,当应用于和类型时,编译器会触发一个断言:

assertion failure for class C1 <:< f, frozen = false
6 |sealed class Foo1 derives BarOf
  |                         ^
  |Exception occurred while executing macro expansion.
  |java.lang.AssertionError: assertion failed: ClassInfo(ThisType(TypeRef(NoPrefix,module class <empty>)), class C0, List(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),class Foo)))
  |     at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
  |     at dotty.tools.dotc.core.Types$TypeBounds.<init>(Types.scala:4918)
  |     at dotty.tools.dotc.core.Types$RealTypeBounds.<init>(Types.scala:4994)
  |     at dotty.tools.dotc.core.Types$TypeBounds$.apply(Types.scala:5038)
  |     at dotty.tools.dotc.core.Types$TypeBounds.derivedTypeBounds(Types.scala:4926)
  |     at dotty.tools.dotc.core.ConstraintHandling.addOneBound(ConstraintHandling.scala:116)
  |     at dotty.tools.dotc.core.ConstraintHandling.addOneBound$(ConstraintHandling.scala:27)
  |     at dotty.tools.dotc.core.ProperGadtConstraint.addOneBound(GadtConstraint.scala:60)
  |     at dotty.tools.dotc.core.ConstraintHandling.addBoundTransitively(ConstraintHandling.scala:170)
  |     at dotty.tools.dotc.core.ConstraintHandling.addBoundTransitively$(ConstraintHandling.scala:27)
  |     at dotty.tools.dotc.core.ProperGadtConstraint.addBoundTransitively(GadtConstraint.scala:60)
  |     at dotty.tools.dotc.core.ProperGadtConstraint.addBound(GadtConstraint.scala:159)
  |     at dotty.tools.dotc.core.TypeComparer.gadtAddLowerBound(TypeComparer.scala:118)
  |     at dotty.tools.dotc.core.TypeComparer.narrowGADTBounds(TypeComparer.scala:1913)
  |     at dotty.tools.dotc.core.TypeComparer.compareGADT$1(TypeComparer.scala:521)
  |     at dotty.tools.dotc.core.TypeComparer.thirdTryNamed$1(TypeComparer.scala:524)
  |     at dotty.tools.dotc.core.TypeComparer.thirdTry$1(TypeComparer.scala:573)
  |     at dotty.tools.dotc.core.TypeComparer.secondTry$1(TypeComparer.scala:504)
  |     at dotty.tools.dotc.core.TypeComparer.compareNamed$1(TypeComparer.scala:313)
  |     at dotty.tools.dotc.core.TypeComparer.firstTry$1(TypeComparer.scala:319)
  |     at dotty.tools.dotc.core.TypeComparer.recur(TypeComparer.scala:1321)
  |     at dotty.tools.dotc.core.TypeComparer.isSubType(TypeComparer.scala:201)
  |     at dotty.tools.dotc.core.TypeComparer.isSubType(TypeComparer.scala:211)
  |     at dotty.tools.dotc.core.TypeComparer.topLevelSubType(TypeComparer.scala:128)
  |     at dotty.tools.dotc.core.TypeComparer$.topLevelSubType(TypeComparer.scala:2729)
  |     at dotty.tools.dotc.core.Types$Type.$less$colon$less(Types.scala:1035)
  |     at scala.quoted.runtime.impl.QuoteMatcher$.$eq$qmark$eq(QuoteMatcher.scala:336)
  |     at scala.quoted.runtime.impl.QuoteMatcher$.treeMatch(QuoteMatcher.scala:129)
  |     at scala.quoted.runtime.impl.QuotesImpl.scala$quoted$runtime$impl$QuotesImpl$$treeMatch(QuotesImpl.scala:3021)
  |     at scala.quoted.runtime.impl.QuotesImpl$TypeMatch$.unapply(QuotesImpl.scala:2991)
  |     at bar.BarOf$.childExpr$1(bar.scala:21)
  |     at bar.BarOf$.$anonfun$6(bar.scala:26)
  |     at scala.collection.immutable.List.map(List.scala:246)
  |     at bar.BarOf$.derivedImpl(bar.scala:26)

该断言由带引号的类型匹配触发:case '[f] =>。但我无法弄清楚我错过了什么。 我的代码合法吗?

  • 如果不是:有什么替代方案?
  • 如果是:是编译器错误吗?

完整的 bar 包源码:

package bar
import scala.quoted.*


trait Bar:
  def children : List[Bar]

trait BarOf[A] extends Bar

object BarOf :
  inline def derived[A]: BarOf[A] =  ${derivedImpl[A]}

  def derivedImpl[A:Type](using Quotes):Expr[BarOf[A]] =
    import quotes.reflect._
    val A           = TypeRepr.of[A]
    val mySymbol    = A.typeSymbol
    val terms       = mySymbol.children

    def childExpr(childSymbol:Symbol) =
      A.memberType(childSymbol).asType match {
        case '[f] => Expr.summon[BarOf[f]]
      } getOrElse {
        report.errorAndAbort(s"ChildType ${childSymbol} of ${mySymbol} does not derive BarOf")
      }

    val termsExpr =  Expr.ofList(terms.map(childExpr))
    '{  new BarOf[A]
        {   def children = ${termsExpr}
        }
     }

测试代码:

import bar.BarOf
  
// works
enum Foo0 derives BarOf :
  case C0

// does not work 
enum Foo1 derives BarOf :
  case C1(x:Int)

// does not work either :
sealed class Foo2 derives BarOf
class C2(x:Int) extends Foo2



@main def run:Unit =
  val x = summon[BarOf[Foo1]]
  println(x.children)

I am doing type class derivation in Scala3.
My typeclass is BarOf[T] extending Bar. T is expected to be an ADT.
Bar has a children method returning the BarOf[X] of the children types of T.

Here the derived method is based on Quotes rather than on Mirrors. This is needed for the rest of my code, which has been removed here.

The essential step to find out the BarOf[X] (for each of the X children types) is based on a quoted type match. (Full code at the end of the post)

A.memberType(childSymbol).asType match {
        case '[f] => Expr.summon[BarOf[f]]
      }

This works fine for product types and particularly case classes.
But, when applying to sum types, the compiler fires an assertion :

assertion failure for class C1 <:< f, frozen = false
6 |sealed class Foo1 derives BarOf
  |                         ^
  |Exception occurred while executing macro expansion.
  |java.lang.AssertionError: assertion failed: ClassInfo(ThisType(TypeRef(NoPrefix,module class <empty>)), class C0, List(TypeRef(ThisType(TypeRef(NoPrefix,module class <empty>)),class Foo)))
  |     at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
  |     at dotty.tools.dotc.core.Types$TypeBounds.<init>(Types.scala:4918)
  |     at dotty.tools.dotc.core.Types$RealTypeBounds.<init>(Types.scala:4994)
  |     at dotty.tools.dotc.core.Types$TypeBounds$.apply(Types.scala:5038)
  |     at dotty.tools.dotc.core.Types$TypeBounds.derivedTypeBounds(Types.scala:4926)
  |     at dotty.tools.dotc.core.ConstraintHandling.addOneBound(ConstraintHandling.scala:116)
  |     at dotty.tools.dotc.core.ConstraintHandling.addOneBound$(ConstraintHandling.scala:27)
  |     at dotty.tools.dotc.core.ProperGadtConstraint.addOneBound(GadtConstraint.scala:60)
  |     at dotty.tools.dotc.core.ConstraintHandling.addBoundTransitively(ConstraintHandling.scala:170)
  |     at dotty.tools.dotc.core.ConstraintHandling.addBoundTransitively$(ConstraintHandling.scala:27)
  |     at dotty.tools.dotc.core.ProperGadtConstraint.addBoundTransitively(GadtConstraint.scala:60)
  |     at dotty.tools.dotc.core.ProperGadtConstraint.addBound(GadtConstraint.scala:159)
  |     at dotty.tools.dotc.core.TypeComparer.gadtAddLowerBound(TypeComparer.scala:118)
  |     at dotty.tools.dotc.core.TypeComparer.narrowGADTBounds(TypeComparer.scala:1913)
  |     at dotty.tools.dotc.core.TypeComparer.compareGADT$1(TypeComparer.scala:521)
  |     at dotty.tools.dotc.core.TypeComparer.thirdTryNamed$1(TypeComparer.scala:524)
  |     at dotty.tools.dotc.core.TypeComparer.thirdTry$1(TypeComparer.scala:573)
  |     at dotty.tools.dotc.core.TypeComparer.secondTry$1(TypeComparer.scala:504)
  |     at dotty.tools.dotc.core.TypeComparer.compareNamed$1(TypeComparer.scala:313)
  |     at dotty.tools.dotc.core.TypeComparer.firstTry$1(TypeComparer.scala:319)
  |     at dotty.tools.dotc.core.TypeComparer.recur(TypeComparer.scala:1321)
  |     at dotty.tools.dotc.core.TypeComparer.isSubType(TypeComparer.scala:201)
  |     at dotty.tools.dotc.core.TypeComparer.isSubType(TypeComparer.scala:211)
  |     at dotty.tools.dotc.core.TypeComparer.topLevelSubType(TypeComparer.scala:128)
  |     at dotty.tools.dotc.core.TypeComparer$.topLevelSubType(TypeComparer.scala:2729)
  |     at dotty.tools.dotc.core.Types$Type.$less$colon$less(Types.scala:1035)
  |     at scala.quoted.runtime.impl.QuoteMatcher$.$eq$qmark$eq(QuoteMatcher.scala:336)
  |     at scala.quoted.runtime.impl.QuoteMatcher$.treeMatch(QuoteMatcher.scala:129)
  |     at scala.quoted.runtime.impl.QuotesImpl.scala$quoted$runtime$impl$QuotesImpl$treeMatch(QuotesImpl.scala:3021)
  |     at scala.quoted.runtime.impl.QuotesImpl$TypeMatch$.unapply(QuotesImpl.scala:2991)
  |     at bar.BarOf$.childExpr$1(bar.scala:21)
  |     at bar.BarOf$.$anonfun$6(bar.scala:26)
  |     at scala.collection.immutable.List.map(List.scala:246)
  |     at bar.BarOf$.derivedImpl(bar.scala:26)

The assertion is triggered by the quoted type match : case '[f] =>. But I cannot figure out what I am missing.
Is my code legal ?

  • if no : what is the alternative ?
  • if yes : is it compiler bug ?

Full bar package source:

package bar
import scala.quoted.*


trait Bar:
  def children : List[Bar]

trait BarOf[A] extends Bar

object BarOf :
  inline def derived[A]: BarOf[A] =  ${derivedImpl[A]}

  def derivedImpl[A:Type](using Quotes):Expr[BarOf[A]] =
    import quotes.reflect._
    val A           = TypeRepr.of[A]
    val mySymbol    = A.typeSymbol
    val terms       = mySymbol.children

    def childExpr(childSymbol:Symbol) =
      A.memberType(childSymbol).asType match {
        case '[f] => Expr.summon[BarOf[f]]
      } getOrElse {
        report.errorAndAbort(s"ChildType ${childSymbol} of ${mySymbol} does not derive BarOf")
      }

    val termsExpr =  Expr.ofList(terms.map(childExpr))
    '{  new BarOf[A]
        {   def children = ${termsExpr}
        }
     }

Test Code:

import bar.BarOf
  
// works
enum Foo0 derives BarOf :
  case C0

// does not work 
enum Foo1 derives BarOf :
  case C1(x:Int)

// does not work either :
sealed class Foo2 derives BarOf
class C2(x:Int) extends Foo2



@main def run:Unit =
  val x = summon[BarOf[Foo1]]
  println(x.children)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ぶ宁プ宁ぶ 2025-01-21 16:21:46

问题并不像我想象的那样出在类型模式匹配上,而是在匹配的符号上。

正确的查找是:

childSymbol.typeRef.asType match {
    case '[f] => Expr.summon[BarOf[f]]
}

而不是:

A.memberType(childSymbol).asType match {
    case '[f] => Expr.summon[BarOf[f]]
}

完整的功能代码:

package bar
import scala.quoted.*


trait Bar:
  def children : List[Bar]

trait BarOf[A] extends Bar

object BarOf :
  inline def derived[A]: BarOf[A] =  ${derivedImpl[A]}

  def derivedImpl[A:Type](using Quotes):Expr[BarOf[A]] =
    import quotes.reflect._
    val A           = TypeRepr.of[A]
    val mySymbol    = A.typeSymbol
    val terms       = mySymbol.children

    def childExpr(childSymbol:Symbol) =
      childSymbol.typeRef.asType match {
        case '[f] => Expr.summon[BarOf[f]]
      } getOrElse {
        report.errorAndAbort(s"ChildType ${childSymbol} of ${mySymbol} does not derive BarOf")
      }

    val termsExpr =  Expr.ofList(terms.map(childExpr))
    '{  new BarOf[A]
        {   def children = ${termsExpr}
            override def toString = ${Expr(s"BarOf[${mySymbol.fullName}]")}
        }
     }

The issue was not in the type pattern matching as I thought, but on the symbol being matched.

The correct look-up is :

childSymbol.typeRef.asType match {
    case '[f] => Expr.summon[BarOf[f]]
}

instead of :

A.memberType(childSymbol).asType match {
    case '[f] => Expr.summon[BarOf[f]]
}

Complete Functional Code :

package bar
import scala.quoted.*


trait Bar:
  def children : List[Bar]

trait BarOf[A] extends Bar

object BarOf :
  inline def derived[A]: BarOf[A] =  ${derivedImpl[A]}

  def derivedImpl[A:Type](using Quotes):Expr[BarOf[A]] =
    import quotes.reflect._
    val A           = TypeRepr.of[A]
    val mySymbol    = A.typeSymbol
    val terms       = mySymbol.children

    def childExpr(childSymbol:Symbol) =
      childSymbol.typeRef.asType match {
        case '[f] => Expr.summon[BarOf[f]]
      } getOrElse {
        report.errorAndAbort(s"ChildType ${childSymbol} of ${mySymbol} does not derive BarOf")
      }

    val termsExpr =  Expr.ofList(terms.map(childExpr))
    '{  new BarOf[A]
        {   def children = ${termsExpr}
            override def toString = ${Expr(s"BarOf[${mySymbol.fullName}]")}
        }
     }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文