scala - 可扩展层次结构的类构建器模式(弱 ADT)
问题描述
TL;博士
给定案例类的层次结构,可以构造实例树:
- 如何通过适当的构建器以类型安全(和用户友好)的方式将其转换为“其他东西”(不接触或更改相应的案例类)?
2021-02-25 更新
我可以让它以某种方式工作
import org.scalajs.dom.console
trait Root
case class Bob(name: String, as: Seq[Root]) extends Root
case class Charles(value: Int, as: Seq[Root]) extends Root
trait Builder[T] {
// can not make T covariant, as this is obj is not in a covariant position
def build(obj: T) : String
}
object Foo {
var r: Map[Root, Builder[Root]] = Map()
def attachBuilder[T <: Root](a: T, builder: Builder[T]) : Unit = {
val e = (a, builder)
r = r + e.asInstanceOf[(Root, Builder[Root])]
}
def b(name : String, as: Root*)(implicit builderB: Builder[Bob]): Bob = {
val b = Bob(name, as)
attachBuilder(b, builderB)
b
}
def c(value: Int, as: Root*)(implicit builderC: Builder[Charles]): Charles = {
val c = Charles(value, as)
attachBuilder(c, builderC)
c
}
def build(obj: Root) : String = {
r(obj).build(obj)
}
}
object UseMe {
implicit val builderB: Builder[Bob] = new Builder[Bob] {
override def build(obj: Bob): String = {
obj.name.toString + obj.as.map(a => Foo.build(a)).mkString(" ")
}
}
implicit val builderC: Builder[Charles] = new Builder[Charles] {
override def build(obj: Charles): String = {
obj.value.toString + obj.as.map(a => Foo.build(a)).mkString(" ")
}
}
def yahoo() : Unit = {
val x = Foo.b("martin", Foo.b("b2"), Foo.c(127))
console.log("This is x: " + Foo.build(x))
}
}
尽管如此,我还是很不满意。想法,我遵循:有一张地图,可以捕捉到 Bob 或 Charles 各自的建造者。仍然,
Builder[T]
不能成为协变的。这可以防止 Builder[Bob] 成为 Builder[Root] 的子类型- 我没有看到 Map 的正确类型签名,所以我不得不进行类型转换。
要求
- Root、Bob、Alice 的层次结构是可扩展的(所以不能被密封)
- 不使用静态类型(通过 Shapeless HList 或类似方法),因为我在进行计算以组装树时根本不会拥有完整的类型。
问题
- 什么是更好的方法?
原帖
序幕
叹息....令人费解的浪费时间........我真的需要你的帮助!
设想
给定一个 ADT
trait A
case class B(name: String, as: Seq[A]) extends A
case class C(value: Int, as: Seq[A]) extends A
预计会延长(未密封)。
此外,假设一个
trait Builder[T] {
def build(obj: T) : String
}
此外,通过下面的代码,我们有“创建者函数”,期望适当的构建器在范围内。
object Foo {
def b(name : String, as: A*)(implicit builderB: Builder[B]): B = {
???
// How to link ADT instance B with builder for B in a type-safe manner?
// How to trigger builder for `as`: Seq[A] ?
}
def c(value: Int, as: A*)(implicit builderC: Builder[C]): C = {
???
// How to link ADT instance C with builder for C in a type-safe manner?
}
}
有了这个,我希望能够,在将适当的构建器定义为隐式 valsB
和C
做
object UseMe {
implicit val builderB: Builder[B] = new Builder[B] {
override def build(obj: B): String = {
obj.toString
// and build the as
}
}
implicit val builderC: Builder[C] = new Builder[C] {
override def build(obj: C): String = {
obj.value.toString
// and also build the as
}
}
val x = Foo.b("martin", Foo.b("b2"), Foo.c(127))
// Questions
// How to create a string representation (that is what the builder is doing) for x
// Something like:
// build(x)
}
有意地,我删除了我在代码中的所有误导性尝试,也不是为了引起任何偏见。
尝试
- 使用动态类型信息(通过
case b: B => ...
)的构建器 impl 正在工作,但正如我希望扩展 ADT 一样,这不是一个选项。 - 我所有通过泛型类型建模的尝试都失败了。(使用 HList(Shapeless)的方法可能是可行的,但不被考虑,因为我认为这可以在普通 Scala 中解决)
问题
- 如何定义方法
Foo
? - 如何解决最适合 ADT 的构建器模式/创建模式?
期待您的回答!
解决方案
推荐阅读
- database - “您输入的表达式要求控件位于活动窗口中”错误 - 找不到它的来源
- node.js - 需要从另一个 NodeJs 应用程序运行 NodeJs 应用程序
- string - GCC编译器的问题
- eclipse - 如何正确编译源代码以输出到库和 com/ 目录?
- r - 生态时间序列模型中参数蛮力估计的替代方法
- django - pre_save 信号中的 datetime.time() 比较问题
- android - 检测设备是否无系统植根
- r - 在网状结构中动态改变 conda 环境
- python - 如何使用计时器更改 Tkinter Python 中的进度条值
- excel - 逆向工程excel公式