Scala / Dotty - смешать черту в существующий объект - PullRequest
3 голосов
/ 10 апреля 2020

Есть ли способ смешать черту в существующий объект в Dotty или Scala?

class SomeClass
trait SomeTrait

// This works, but it's not what I'm looking for:
new SomeClass with SomeTrait

// This is what I'm looking for, but it breaks:
val someClass = new SomeClass
someClass with SomeTrait

Этот ответ предоставляет решение для макроса, но ему 7 лет и я надеюсь (скрестив пальцы!) на что-то попроще.

Ответы [ 3 ]

2 голосов
/ 10 апреля 2020

Вам все еще нужен макрос, если вы хотите, чтобы класс

class SomeClass1 extends SomeClass with SomeTrait

генерировался автоматически.

Я проверил, и макрос все еще работает (с незначительными изменениями)

def toPersisted[T](instance: T, id: Long): T with Persisted = macro impl[T]

def impl[T: c.WeakTypeTag](c: blackbox.Context)(instance: c.Tree, id: c.Tree): c.Tree = {
  import c.universe._

  val typ = weakTypeOf[T]
  val symbol = typ.typeSymbol
  if (!symbol.asClass.isCaseClass)
    c.abort(c.enclosingPosition, s"toPersisted only accepts case classes, you provided $typ")

  val accessors = typ.members.sorted.collect { case x: TermSymbol if x.isCaseAccessor && x.isMethod => x }
  val fieldNames = accessors map (_.name)

  val instanceParam = q"val instance: $typ"
  val idParam = q"${Modifiers(Flag.PARAMACCESSOR)} val id: Long"
  val superArgs = fieldNames map (fieldName => q"instance.$fieldName")
  val ctor =
    q"""def ${termNames.CONSTRUCTOR}($instanceParam, $idParam) = {
      super.${termNames.CONSTRUCTOR}(..$superArgs)
      ()
    }"""
  val idVal = idParam.duplicate
  val tmpl = Template(List(tq"$typ", tq"Persisted"), noSelfType, List(idVal, ctor))
  val cname = TypeName(c.freshName(symbol.name.toString + "$Persisted"))
  val cdef = ClassDef(NoMods, cname, Nil, tmpl)

  q"""
     $cdef
     new $cname($instance, $id)
    """
}

case class MyClass(i: Int, s: String)

val x = MyClass(1, "a")

val y = toPersisted(x, 2L)

y.i // 1
y.s // a
y.id // 2
2 голосов
/ 10 апреля 2020

А как насчет использования классов типов?

Из примера, который вы предоставили в комментарии к своему вопросу:

trait Organism
trait Winged[O <: Organism]
trait Legged[O <: Organism]

class Dog extends Organism
object Dog {
   implicit val legged: Legged[Dog] = new Legged[Dog] { ... }
}

class Fly extends Organism
object Fly {
   implicit val winged: Winged[Fly] = new Winged[Fly] { ... }
   implicit val legged: Legged[Fly] = new Legged[Fly] { ... }
}

Это очень гибкий подход, который позволяет вам определять свойства Legged и Winged при проектировании. конкретный организм, или добавить их позже через импликации за пределами соответствующих сопутствующих объектов. Вы можете заставить организм всегда иметь ноги / крылья, предоставляя неявное в объекте-компаньоне, или оставить это на усмотрение пользователей вашего кода.

Затем вы можете определить

// Only Winged organisms (ie. `O` for which `Winged[O]` is available implicitly
def makeItFly[O <: Organism : Winged](o: O) 
2 голосов
/ 10 апреля 2020

Взгляните на, казалось бы, заброшенную, но довольно свежую библиотеку zio-делегат :

import zio.delegate._

class SomeClass

trait SomeTrait {
  def test() = println("It just works!")
}

val someClass = new SomeClass

val result: SomeClass with SomeTrait =
  Mix[SomeClass, SomeTrait].mix(someClass, new SomeTrait {})

result.test()

Она все еще основана на макросах, и в Scala редко можно использовать миксины до такой степени. Zio полностью изменил схему, IIU C.

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