Указать имя объекта дела - PullRequest
0 голосов
/ 21 апреля 2020

С учетом этого кода:

sealed trait Parent

case object GetOne extends Parent
case object GetTwo extends Parent

Возможно ли в Scala применить эти ограничения:

  1. Parent можно расширить только на case object
  2. Имя ребенка case object из Parent должно начинаться с Get.

Возможно ли это?

Ответы [ 3 ]

4 голосов
/ 21 апреля 2020

Попробуйте макросы (с Shapeless)

import shapeless.ops.{coproduct, hlist}
import shapeless.{Coproduct, HList, LabelledGeneric}
import shapeless.ops.union.{Keys, Values}

def check[A] = new PartiallyApplied[A]

class PartiallyApplied[A] {
  def apply[C <: Coproduct, K <: HList, V <: Coproduct]()(implicit
    labelledGeneric: LabelledGeneric.Aux[A, C],
    keys: Keys.Aux[C, K],
    values: Values.Aux[C, V],
    allKeysStartWithGet: hlist.LiftAll[StartsWithGet, K],
    allValuesAreObjects: coproduct.LiftAll[IsObject, V]
  ) = null
}

import shapeless.Witness
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait StartsWithGet[S]

object StartsWithGet {
  implicit def mkStartsWithGet[S <: Symbol]: StartsWithGet[S] = macro impl[S]

  def impl[S <: Symbol : c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val typ = weakTypeOf[S]
    val witness = c.inferImplicitValue(
      c.typecheck(tq"_root_.shapeless.Witness.Aux[$typ]", mode = c.TYPEmode).tpe,
      silent = false
    )
    val str = c.eval(c.Expr[Witness.Lt[scala.Symbol]](
      c.untypecheck(witness.duplicate)
    )).value.name

    if (str.startsWith("Get"))
      q"new StartsWithGet[$typ] {}"
    else c.abort(c.enclosingPosition, s"$str doesn't start with Get")
  }
}

trait IsObject[A]

object IsObject {
  implicit def mkIsObject[A]: IsObject[A] = macro impl[A]

  def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val typ = weakTypeOf[A]
    if (typ.typeSymbol.isModuleClass)
      q"new IsObject[$typ] {}"
    else c.abort(c.enclosingPosition, s"$typ is not object")
  }
}

sealed trait Parent 
case object GetOne extends Parent
case object GetTwo extends Parent
check[Parent]() // compiles

sealed trait Parent
case object GetOne extends Parent
case object Two extends Parent
check[Parent]() // doesn't compile

sealed trait Parent
case object GetOne extends Parent
case class GetTwo() extends Parent
check[Parent]() // doesn't compile
4 голосов
/ 21 апреля 2020

Родитель может быть расширен только объектом объекта

. Вы можете приблизиться, используя Singleton .
(как упомянутый @MateuszKubuszok)

Вот пример:

sealed trait Foo extends Product with Serializable { self: Singleton => }

Тогда это работает:

final case object A extends Foo
final case object B extends Foo

И это не так:

final case object A extends Foo
final case class B(blah: String) extends Foo

Дочерний объект case родительского объекта должен иметь свои имена, начинающиеся с Get.

Не используется стандарт Scala.
Может быть, с макросами или чем-то в этом роде, но на самом деле кажется странным требованием; Планируете ли вы получить эти случаи с помощью размышлений? или в чем причина такого желания?

(в любом случае кажется, что это может быть лучше обработано обзорами кода и, возможно, правилом скалификса)

3 голосов
/ 21 апреля 2020

Родитель может быть расширен только объектом case

В Scala 3 вы можете определить перечисление

enum Parent {
  case GetOne, GetTwo
}

, которое вызывает члены должны быть объектами дела.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...