将 varargs 参数限制为某些分离类型
Restrict varargs parameter to certain disjunct types
我目前正在尝试编写 DSL 的 scala 类型系统。我想要的是以下内容:
// custom dsl
val out = TagA(
TagB(),
TagC(
TagC()
TagA()
),
)
TagA
接受 0..n
TagB
或 TagC
参数,但不接受 TagA
TagB
不接受任何参数
TagC
接受 0..n
TagC
或 TagA
参数,但不接受 TagB
所以基本上我试图在 varargs 参数中传递各种分离类型。我想我可以限制每个 Tag
使用类型类接受的具体类型。因此,我将 TagA
、TagB
和 TagC
建模如下:
case class TagA[A: TagAChildren](children: A*)
case object TagB
case class TagC[A: TagCChildren](children: A*)
sealed trait TagAChild[-T]
object TagAChildren {
implicit object TagBWitness extends TagAChild[TagB]
implicit object TagCWitness extends TagAChild[TagC]
}
sealed trait TagCChild[-T]
object TagCChildren {
implicit object TagCWitness extends TagCChild[TagC]
implicit object TagAWitness extends TagCChild[TagA]
}
现在,如果我传递的是单一类型,这就可以正常工作了。例如:
TagA() // compiles
TagA( // compiles
TagB(),
TagB(),
)
但是如果我混合使用 TagB
和 TagC
:
编译失败
TagA(
TagB(),
TagC(),
)
// could not find implicit value for evidence parameter of type TagAChild[Product with java.io.Serializable]
显然编译器找不到隐含的。我想我在这里对编译器的要求有点太多了……有人能想出办法让这项工作成功吗?非常感谢任何提示或替代方法。
这就是我想出的:
case class TagA(children: TagMagnet[TagA]*)
case object TagB
case class TagC(children: TagMagnet[TagC]*)
sealed trait TagMagnet[A] {
type Result
def apply(): Result
}
trait TagMagnetBuilder[A] {
def build[R](value: R): TagMagnet[A] = new TagMagnet[A] {
override type Result = R
override def apply(): Result = value
}
}
object TagAMagnet extends TagMagnetBuilder[TagA] {
implicit def tagBMagnet(tag: TagB): TagMagnet[TagA] = build(tag)
implicit def tagCMagnet(tag: TagC): TagMagnet[TagA] = build(tag)
}
object TagCMagnet extends TagMagnetBuilder[TagC] {
implicit def tagCMagnet(tag: TagC): TagMagnet[TagC] = build(tag)
implicit def tagAMagnet(tag: TagA): TagMagnet[TagC] = build(tag)
}
对于任何给定的 Tag
类型,我可以扩展 TagMagnetBuilder
并定义该标签应该接受的类型。有了上面的定义,下面的片段编译没有问题:
val out = TagA(
TagB(),
TagC(
TagC()
TagA()
),
)
我目前正在尝试编写 DSL 的 scala 类型系统。我想要的是以下内容:
// custom dsl
val out = TagA(
TagB(),
TagC(
TagC()
TagA()
),
)
TagA
接受0..n
TagB
或TagC
参数,但不接受TagA
TagB
不接受任何参数TagC
接受0..n
TagC
或TagA
参数,但不接受TagB
所以基本上我试图在 varargs 参数中传递各种分离类型。我想我可以限制每个 Tag
使用类型类接受的具体类型。因此,我将 TagA
、TagB
和 TagC
建模如下:
case class TagA[A: TagAChildren](children: A*)
case object TagB
case class TagC[A: TagCChildren](children: A*)
sealed trait TagAChild[-T]
object TagAChildren {
implicit object TagBWitness extends TagAChild[TagB]
implicit object TagCWitness extends TagAChild[TagC]
}
sealed trait TagCChild[-T]
object TagCChildren {
implicit object TagCWitness extends TagCChild[TagC]
implicit object TagAWitness extends TagCChild[TagA]
}
现在,如果我传递的是单一类型,这就可以正常工作了。例如:
TagA() // compiles
TagA( // compiles
TagB(),
TagB(),
)
但是如果我混合使用 TagB
和 TagC
:
TagA(
TagB(),
TagC(),
)
// could not find implicit value for evidence parameter of type TagAChild[Product with java.io.Serializable]
显然编译器找不到隐含的。我想我在这里对编译器的要求有点太多了……有人能想出办法让这项工作成功吗?非常感谢任何提示或替代方法。
这就是我想出的:
case class TagA(children: TagMagnet[TagA]*)
case object TagB
case class TagC(children: TagMagnet[TagC]*)
sealed trait TagMagnet[A] {
type Result
def apply(): Result
}
trait TagMagnetBuilder[A] {
def build[R](value: R): TagMagnet[A] = new TagMagnet[A] {
override type Result = R
override def apply(): Result = value
}
}
object TagAMagnet extends TagMagnetBuilder[TagA] {
implicit def tagBMagnet(tag: TagB): TagMagnet[TagA] = build(tag)
implicit def tagCMagnet(tag: TagC): TagMagnet[TagA] = build(tag)
}
object TagCMagnet extends TagMagnetBuilder[TagC] {
implicit def tagCMagnet(tag: TagC): TagMagnet[TagC] = build(tag)
implicit def tagAMagnet(tag: TagA): TagMagnet[TagC] = build(tag)
}
对于任何给定的 Tag
类型,我可以扩展 TagMagnetBuilder
并定义该标签应该接受的类型。有了上面的定义,下面的片段编译没有问题:
val out = TagA(
TagB(),
TagC(
TagC()
TagA()
),
)