Scala3:通过元编程创建类型?

问题描述 投票:0回答:3

我正在使用 scala3 进行编码,利用编程结构类型。 结构类型适用于mimic现有案例类: 他们的定义是纯粹的样板, 因此,通过元编程来制作它们的诱惑。

我了解如何制作函数实现,通常是通过类型类派生。 但在这里我们正在尝试制作一个(结构)类型

这在 scala2 中是可能的,通过类宏注释,但这些在 scala3 中已经消失了。 有办法吗?如果是这样怎么办?

下面的代码是我想要获得的结果:


// Library part
trait View extends scala.Selectable :
  def selectDynamic(key:String) =
    println(s"$key is being looked up")
    ???


// DSL Definition part
case class SomeDefWithInt   ( i : Int    )
case class SomeDefWithString( s : String )

// Boiler-plate code 
type ViewOf[M] = M match
  case SomeDefWithInt    => View { def i : Int    }
  case SomeDefWithString => View { def s : String }

// Mockup usage
class V extends View
val v = V()

v.asInstanceOf[ViewOf[SomeDefWithInt   ]].i
v.asInstanceOf[ViewOf[SomeDefWithString]].s

是否可以创建任意案例类 M 的 ViewOf[M] ?

谢谢你!

scala metaprogramming scala-macros scala-3
3个回答
5
投票

以防万一,这就是我将

ViewOf
隐藏在类型类中的意思(类型类是匹配类型的替代方法)。遗憾的是,在 Scala 3 中这是冗长的。

(版本1)

import scala.annotation.experimental
import scala.quoted.{Expr, Quotes, Type, quotes}

// Library part
trait View extends Selectable {
  def applyDynamic(key: String)(args: Any*): Any = {
    println(s"$key is being looked up with $args")
    if (key == "i") 1
    else if (key == "s") "a"
    else ???
  }

  def selectDynamic(key: String): Any = {
    println(s"$key is being looked up")
    if (key == "i1") 2
    else if (key == "s1") "b"
    else ???
  }
}

// type class
trait ViewOf[M <: Product] {
  type Out <: View
}

object ViewOf {
  transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}

  @experimental // because .newClass is @experimental
  def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
    import quotes.reflect.*

    extension (symb: Symbol) {
      def setFlags(flags: Flags): Symbol = {
        given dotty.tools.dotc.core.Contexts.Context =
          quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
        symb.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
          .denot.setFlag(flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet])
        symb
      }
    }

    def newType(cls: Symbol, name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags, privateWithin: Symbol = Symbol.noSymbol): Symbol = {
      given dotty.tools.dotc.core.Contexts.Context =
        quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
      import dotty.tools.dotc.core.Decorators.toTypeName
      dotty.tools.dotc.core.Symbols.newSymbol(
        cls.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol],
        name.toTypeName,
        flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet],
        tpe.asInstanceOf[dotty.tools.dotc.core.Types.Type],
        privateWithin.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
      ).asInstanceOf[Symbol]
    }

    val M = TypeRepr.of[M]
    val fields = M.typeSymbol.caseFields

    val viewImplDecls = (cls: Symbol) =>
      fields.flatMap(fieldSymb =>
        Seq(
          Symbol.newMethod(cls, fieldSymb.name, MethodType(Nil)(_ => Nil, _ => M.memberType(fieldSymb)), // vararg? MatchError: Inlined
            Flags.Deferred, privateWithin = Symbol.noSymbol),
          Symbol.newVal(cls, fieldSymb.name + "1", M.memberType(fieldSymb),
            Flags.Deferred, privateWithin = Symbol.noSymbol)
        )
      )

    val viewImplParents = List(TypeTree.of[AnyRef], TypeTree.of[View])

    val viewImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewImpl", viewImplParents.map(_.tpe), viewImplDecls, selfType = None)
      .setFlags(Flags.Trait)
    val methodDefs = fields.flatMap(fieldSymb => {
      val methodSymb = viewImplCls.declaredMethod(fieldSymb.name).head
      val valSymb = viewImplCls.fieldMember(fieldSymb.name + "1")
      Seq(
        DefDef(methodSymb, _ => None),
        ValDef(valSymb, None)
      )
    })
    val viewImplClsDef = ClassDef(viewImplCls, viewImplParents, body = methodDefs)

    val viewOfImplDecls = (cls: Symbol) => List(newType(cls, "Out",
      TypeBounds(viewImplCls.typeRef, viewImplCls.typeRef), Flags.Override))

    val viewOfTypeTree = TypeTree.of[ViewOf[M]]
    val viewOfImplParents = List(TypeTree.of[AnyRef], viewOfTypeTree)

    val viewOfImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewOfImpl", viewOfImplParents.map(_.tpe), viewOfImplDecls, selfType = None)
    val outSymb = viewOfImplCls.declaredType("Out").head

    val outTypeDef = TypeDef(outSymb)
    val viewOfImplClsDef = ClassDef(viewOfImplCls, viewOfImplParents, body = List(outTypeDef))
    val newViewOfImpl = Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil)

    val res = Block(List(viewImplClsDef, viewOfImplClsDef), newViewOfImpl).asExprOf[ViewOf[M]]
    println(res.show + "=" + res.asTerm.show(using Printer.TreeStructure))
    res
  }
}

extension (v: View) {
  def refine[M <: Product](using viewOf: ViewOf[M]): viewOf.Out = v.asInstanceOf[viewOf.Out]
}
// DSL Definition part
case class SomeDefWithInt   ( i : Int    )
case class SomeDefWithString( s : String )

// Mockup usage
class V extends View
val v = V()

println(v.refine[SomeDefWithInt].i())
// i is being looked up with ArraySeq()
// 1
println(v.refine[SomeDefWithString].s())
// s is being looked up with ArraySeq()
// a
println(v.refine[SomeDefWithInt].i1)
// i1 is being looked up
// 2
println(v.refine[SomeDefWithString].s1)
// s1 is being looked up
// b

//scalac: {
//  trait ViewImpl extends java.lang.Object with Macros.View {
//    def i(): scala.Int
//    val i1: scala.Int
//  }
//  class ViewOfImpl extends java.lang.Object with Macros.ViewOf[App.SomeDefWithInt] {
//    type Out // actually, type Out = ViewImpl
//  }
//  new ViewOfImpl()
//}=Block(List(ClassDef("ViewImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(DefDef("i", List(TermParamClause(Nil)), Inferred(), None), ValDef("i1", Inferred(), None))), ClassDef("ViewOfImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(TypeDef("Out", TypeBoundsTree(Inferred(), Inferred()))))), Apply(Select(New(Inferred()), "<init>"), Nil))

//scalac: {
//  trait ViewImpl extends java.lang.Object with Macros.View {
//    def s(): scala.Predef.String
//    val s1: scala.Predef.String
//  }
//  class ViewOfImpl extends java.lang.Object with Macros.ViewOf[App.SomeDefWithString] {
//    type Out // actually, type Out = ViewImpl
//  }
//  new ViewOfImpl()
//}=Block(List(ClassDef("ViewImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(DefDef("s", List(TermParamClause(Nil)), Inferred(), None), ValDef("s1", Inferred(), None))), ClassDef("ViewOfImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(TypeDef("Out", TypeBoundsTree(Inferred(), Inferred()))))), Apply(Select(New(Inferred()), "<init>"), Nil))

ViewOf[M]
旨在由 DSL 用户使用,因此无法将其隐藏在派生类型类中。

不确定我是否理解。


使用 Scala 3 宏重写方法

`tq` 相当于 Scala 3 宏

如何在Dotty中用宏生成类?

如何在scala 3宏的引用语法中拼接多个表达式?

如何在点宏中访问案例类的参数列表

https://github.com/lampepfl/dotty/discussions/14056


类型类的另一种实现(使用细化类型而不是特征类型)

(版本2)

trait ViewOf[M <: Product] {
  type Out <: View
}

object ViewOf {
  transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}

  @experimental // because .newClass is @experimental
  def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
    import quotes.reflect.*

    def makeRefinement(parent: TypeRepr, names: List[String], infos: List[TypeRepr]): TypeRepr =
      names.zip(infos).foldLeft(parent){ case (acc, (name, tpe)) => Refinement(acc, name, tpe) }

    def newType(cls: Symbol, name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags, privateWithin: Symbol = Symbol.noSymbol): Symbol = {
      given dotty.tools.dotc.core.Contexts.Context =
        quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
      import dotty.tools.dotc.core.Decorators.toTypeName
      dotty.tools.dotc.core.Symbols.newSymbol(
        cls.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol],
        name.toTypeName,
        flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet],
        tpe.asInstanceOf[dotty.tools.dotc.core.Types.Type],
        privateWithin.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
      ).asInstanceOf[Symbol]
    }

    val M = TypeRepr.of[M]
    val fields = M.typeSymbol.caseFields

    val fieldNames = fields.flatMap(fieldSymb => Seq(fieldSymb.name, fieldSymb.name + "1"))
    val fieldMethodTypes = fields.flatMap(fieldSymb => Seq(
      MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => M.memberType(fieldSymb)),
      ByNameType(M.memberType(fieldSymb)))
    )
    val refinement = makeRefinement(TypeRepr.of[View], fieldNames, fieldMethodTypes)

    val viewOfImplDecls = (cls: Symbol) => List(newType(cls, "Out",
      TypeBounds(refinement, refinement), Flags.Override))

    val viewOfTypeTree = TypeTree.of[ViewOf[M]]
    val viewOfImplParents = List(TypeTree.of[AnyRef], viewOfTypeTree)

    val viewOfImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewOfImpl", viewOfImplParents.map(_.tpe), viewOfImplDecls, selfType = None)
    val outSymb = viewOfImplCls.declaredType("Out").head

    val outTypeDef = TypeDef(outSymb)
    val viewOfImplClsDef = ClassDef(viewOfImplCls, viewOfImplParents, body = List(outTypeDef))
    val newViewOfImpl = Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil)

    val res = Block(List(viewOfImplClsDef), newViewOfImpl).asExprOf[ViewOf[M]]
    println(res.show + "=" + res.asTerm.show(using Printer.TreeStructure))
    res
  }
}
println(v.refine[SomeDefWithInt].i(10, "x", true))
//i is being looked up with ArraySeq((10,x,true))
//1
println(v.refine[SomeDefWithString].s(20, "y", 30L))
//s is being looked up with ArraySeq((20,y,30))
//a
println(v.refine[SomeDefWithInt].i1)
//i1 is being looked up
//2
println(v.refine[SomeDefWithString].s1)
//s1 is being looked up
//b

//scalac: {
//  class ViewOfImpl extends java.lang.Object with Macros.ViewOf[App.SomeDefWithInt] {
//    type Out // actually, type Out = View {def i(args: Any*): Int; def i1: Int}
//  }
//  new ViewOfImpl()
//}=Block(List(ClassDef("ViewOfImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(TypeDef("Out", TypeBoundsTree(Inferred(), Inferred()))))), Apply(Select(New(Inferred()), "<init>"), Nil))

我们也可以使用

Mirror
代替反射

(版本3)

trait ViewOf[M <: Product] {
  type Out <: View
}

object ViewOf {
  transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}

  def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
    import quotes.reflect.*

    def makeRefinement(parent: TypeRepr, namesAndTypes: List[(String, TypeRepr)]): TypeRepr =
      namesAndTypes.foldLeft(parent) { case (acc, (name, tpe)) => Refinement(acc, name, tpe) }

    def mkNamesAndTypes[mels: Type, mets: Type]: List[(String, TypeRepr)] =
      (Type.of[mels], Type.of[mets]) match {
        case ('[EmptyTuple], '[EmptyTuple]) => Nil
        case ('[mel *: melTail], '[met *: metTail] ) => {
          val name = Type.valueOfConstant[mel].get.toString
          val name1 = name + "1"
            //scala.MatchError: Inlined(Ident(Macros$),List(),Apply(Select(New(Select(Select(Select(Ident(scala),annotation),internal),Repeated)),<init>),List())) (of class dotty.tools.dotc.ast.Trees$Inlined)
          //val methodType = MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => TypeRepr.of[met])
          val methodType = MethodType(Nil)(_ => Nil, _ => TypeRepr.of[met])
          val methodType1 = ByNameType(TypeRepr.of[met])
          (name, methodType) :: (name1, methodType1) :: mkNamesAndTypes[melTail, metTail]
        }
      }

    val namesAndTypes = Expr.summon[Mirror.ProductOf[M]].get match {
      case '{ $m: Mirror.ProductOf[M] { type MirroredElemLabels = mels; type MirroredElemTypes = mets } } =>
        mkNamesAndTypes[mels, mets]
    }

    val res = makeRefinement(TypeRepr.of[View], namesAndTypes).asType match {
      case '[tpe] =>
        '{
          new ViewOf[M] {
            type Out = tpe
          }
        }
    }

    println(res.show)
    res
  }
}

不幸的是,由于额外的类型归属,这不起作用(

Expr
失去了类型细化)

//scalac: {
//  final class $anon() extends Macros.ViewOf[App.SomeDefWithInt] {
//    type Out = Macros.View {
//      def i(): scala.Int
//      def i1: scala.Int
//    }
//  }
//
//  (new $anon(): Macros.ViewOf[App.SomeDefWithInt])  // <--- HERE!!!
//}

https://github.com/lampepfl/dotty/issues/15566(对于结构细化,即 defs,它们的损失似乎是预期的行为,但类型细化损失可能是一个错误)

所以,至少有一次我们必须使用低级

newClass
来避免类型归属

(版本4)

trait ViewOf[M <: Product] {
  type Out <: View
}

object ViewOf {
  transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}

  @experimental // because .newClass is @experimental
  def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
    import quotes.reflect.*

    def makeRefinement(parent: TypeRepr, namesAndTypes: List[(String, TypeRepr)]): TypeRepr =
      namesAndTypes.foldLeft(parent) { case (acc, (name, tpe)) => Refinement(acc, name, tpe) }

    def mkNamesAndTypes[mels: Type, mets: Type]: List[(String, TypeRepr)] =
      (Type.of[mels], Type.of[mets]) match {
        case ('[EmptyTuple], '[EmptyTuple]) => Nil
        case ('[mel *: melTail], '[met *: metTail] ) => {
          val name = Type.valueOfConstant[mel].get.toString
          val name1 = name + "1"
          val methodType = MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => TypeRepr.of[met])
          val methodType1 = ByNameType(TypeRepr.of[met])
          (name, methodType) :: (name1, methodType1) :: mkNamesAndTypes[melTail, metTail]
        }
      }

    val namesAndTypes = Expr.summon[Mirror.ProductOf[M]].get match {
      case '{ $m: Mirror.ProductOf[M] { type MirroredElemLabels = mels; type MirroredElemTypes = mets } } =>
        mkNamesAndTypes[mels, mets]
    }

    val refinement = makeRefinement(TypeRepr.of[View], namesAndTypes)

    def newType(cls: Symbol, name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags, privateWithin: Symbol = Symbol.noSymbol): Symbol = {
      given dotty.tools.dotc.core.Contexts.Context =
        quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
      import dotty.tools.dotc.core.Decorators.toTypeName
      dotty.tools.dotc.core.Symbols.newSymbol(
        cls.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol],
        name.toTypeName,
        flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet],
        tpe.asInstanceOf[dotty.tools.dotc.core.Types.Type],
        privateWithin.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
      ).asInstanceOf[Symbol]
    }

    val viewOfImplDecls = (cls: Symbol) => List(newType(cls, "Out",
      TypeBounds(refinement, refinement),
      Flags.Override))

    val viewOfTypeTree = TypeTree.of[ViewOf[M]]
    val viewOfImplParents = List(TypeTree.of[AnyRef], viewOfTypeTree)

    val viewOfImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewOfImpl", viewOfImplParents.map(_.tpe), viewOfImplDecls, selfType = None)
    val outSymb = viewOfImplCls.declaredType("Out").head

    val outTypeDef = TypeDef(outSymb)
    val viewOfImplClsDef = ClassDef(viewOfImplCls, viewOfImplParents, body = List(outTypeDef))
      // this would be an extra type ascription to be avoided
    // val newViewOfImpl = Typed(Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil), TypeTree.of[ViewOf[M]])
    val newViewOfImpl = Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil)

    val res = Block(List(viewOfImplClsDef), newViewOfImpl).asExprOf[ViewOf[M]]
    println(res.show + "=" + res.asTerm.show(using Printer.TreeStructure))
    res
  }
}

0
投票

下面的解决方案实际上是@dmytro 提供的(版本3),稍作修改即可使其工作。 它纯粹是基于Mirror,而构建的一种Refinement。 没有scala3-compiler库,没有@experimental。 出色地推出白盒宏。

package refine
import scala.quoted.{Quotes,Type,Expr}

// Library part
trait View extends Selectable :
  def applyDynamic(key: String)(args: Any*): Any =
    println(s"$key is being looked up with $args")
    key match
      case "i" => 1
      case "s" => "a"
      case _   => ???

  def selectDynamic(key: String): Any =
    println(s"$key is being looked up")
    key match 
      case "i1" => 2 
      case "s1" => "b"
      case _    => ???
end View

// type class
trait ViewOf[M <: Product]:
  type Out <: View

object ViewOf :
  transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}

  def givenImpl[M <: Product : Type](using quotes:Quotes) : Expr[ViewOf[M]]  =
    import quotes.reflect.*
    import scala.deriving.Mirror

    def makeRefinement(parent: TypeRepr, namesAndTypes: List[(String, TypeRepr)]): TypeRepr =
      namesAndTypes.foldLeft(parent) { case (acc, (name, tpe)) => Refinement(acc, name, tpe) }

    def mkNamesAndTypes[mels: Type, mets: Type]: List[(String, TypeRepr)] =
      (Type.of[mels], Type.of[mets]) match {
        case ('[EmptyTuple], '[EmptyTuple]) => Nil
        case ('[mel *: melTail], '[met *: metTail] ) => {
          val name = Type.valueOfConstant[mel].get.toString
          val name1 = name + "1"
            //scala.MatchError: Inlined(Ident(Macros$),List(),Apply(Select(New(Select(Select(Select(Ident(scala),annotation),internal),Repeated)),<init>),List())) (of class dotty.tools.dotc.ast.Trees$Inlined)
          //val methodType = MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => TypeRepr.of[met])
          val methodType = MethodType(Nil)(_ => Nil, _ => TypeRepr.of[met])
          val methodType1 = ByNameType(TypeRepr.of[met])
          (name, methodType) :: (name1, methodType1) :: mkNamesAndTypes[melTail, metTail]
        }
      }

    val namesAndTypes = Expr.summon[Mirror.ProductOf[M]].get match
      case '{ $m: Mirror.ProductOf[M] { type MirroredElemLabels = mels; type MirroredElemTypes = mets } } =>
        mkNamesAndTypes[mels, mets]

    val res = makeRefinement(TypeRepr.of[View], namesAndTypes).asType match
      case '[tpe] => '{
          ( new ViewOf[M]:
              type Out = tpe
          ) : ViewOf[M] { type Out = tpe }
       }
    println(res.show)
    res
  end givenImpl
end ViewOf

extension (v: View) {
  def refine[M <: Product](using viewOf: ViewOf[M]): viewOf.Out = v.asInstanceOf[viewOf.Out]
}

遗漏的一点是:匿名类的推断预计不会被细化(除非该类是

Selectable
,而
ViewOf[_]
不是这种情况)。需要具有正确细化类型的手动类型归属。


0
投票

我正在尝试使用 Jean-Jacques Lecler 编写的版本 3。我已将代码复制并粘贴到一个文件中。在另一个我有:

package examples.select

import examples.select.View
import examples.select.ViewOf
import examples.select.ViewOf.given


// DSL Definition part
case class SomeDefWithInt   ( i : Int    )
case class SomeDefWithString( s : String )

// Mockup usage
class V extends View
val v = V()

//val v1 = v.refine(using ViewOf.mkViewOf[V])
val v1 = v.refine
//val v2 = v.refine[View]

我收到以下错误:


value refine is not a member of examples.select.V.
An extension method was tried, but could not be fully constructed:

    examples.select.refine(examples.select.v)[M](???)

    failed with:

        No given instance of type examples.select.ViewOf[M] was found for parameter viewOf of method refine in package examples.select.
        I found:
        
            examples.select.ViewOf.mkViewOf[Product]
        
        But Exception occurred while executing macro expansion.
        java.util.NoSuchElementException: None.get
            at scala.None$.get(Option.scala:627)
            at scala.None$.get(Option.scala:626)
            at examples.select.ViewOf$.givenImpl(View.scala:52)
        
        .
        
        where:    M is a type variable with constraint <: Product
        

如果将类型强制为

View
,我会得到相同的错误。我正在 3.3.3 上测试这个。错误在于以下传票中:

    val namesAndTypes = Expr.summon[Mirror.ProductOf[M]].get match
      case '{ $m: Mirror.ProductOf[M] { type MirroredElemLabels = mels; type MirroredElemTypes = mets } } =>
        mkNamesAndTypes[mels, mets]

我在这里缺少什么。

蒂亚

© www.soinside.com 2019 - 2024. All rights reserved.