Scala / Dotty - 将特征混合到现有对象中
Scala / Dotty - Mix a trait into an EXISTING object
有没有办法将特征混合到 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
This 答案提供了一个宏解决方案,但它已有 7 年历史,我希望(祈祷!)更简单的东西。
看看看似废弃但相当新的图书馆 zio-delegate:
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 中很少使用 mixins 到那种程度。 Zio完全换了个花样,IIUC.
如果你想要,你还需要一个宏class
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
改用类型类怎么样?
根据您在问题评论中提供的示例:
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
属性,或者稍后通过相应伴随对象之外的隐式添加它们。您可以通过在伴随对象中提供隐式来强制有机体始终具有 legs/wings,或者将其留给代码的用户。
然后您可以定义
// Only Winged organisms (ie. `O` for which `Winged[O]` is available implicitly
def makeItFly[O <: Organism : Winged](o: O)
有没有办法将特征混合到 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
This 答案提供了一个宏解决方案,但它已有 7 年历史,我希望(祈祷!)更简单的东西。
看看看似废弃但相当新的图书馆 zio-delegate:
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 中很少使用 mixins 到那种程度。 Zio完全换了个花样,IIUC.
如果你想要,你还需要一个宏class
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
改用类型类怎么样?
根据您在问题评论中提供的示例:
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
属性,或者稍后通过相应伴随对象之外的隐式添加它们。您可以通过在伴随对象中提供隐式来强制有机体始终具有 legs/wings,或者将其留给代码的用户。
然后您可以定义
// Only Winged organisms (ie. `O` for which `Winged[O]` is available implicitly
def makeItFly[O <: Organism : Winged](o: O)