将 varargs 参数限制为某些分离类型

Posted

技术标签:

【中文标题】将 varargs 参数限制为某些分离类型【英文标题】:Restrict varargs parameter to certain disjunct types 【发布时间】:2021-05-16 04:46:09 【问题描述】:

我目前正在尝试编写 DSL 的 scala 类型系统。我想要的是以下内容:

// custom dsl
val out = TagA(
  TagB(),
  TagC(
    TagC()
    TagA()
  ),
)
TagA 接受 0..n TagBTagC 参数,但不接受 TagA TagB 不接受任何参数 TagC 接受 0..n TagCTagA 参数,但不接受 TagB

所以基本上我试图在 varargs 参数中传递各种分离类型。我想我可以限制每个Tag 使用类型类接受的具体类型。因此我对TagATagBTagC建模如下:

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(),
)

但是,如果我混合使用例如 TagBTagC,编译会失败:

TagA(
  TagB(),
  TagC(),
)

// could not find implicit value for evidence parameter of type TagAChild[Product with java.io.Serializable]

显然编译器找不到隐含的。我想我在这里对编译器的要求有点太多了......有人能想出一些办法来完成这项工作吗?非常感谢任何提示或替代方法。

【问题讨论】:

在 TagAChildren 中,使用 TagAChild 作为 TabBWitness 和 TagCWitness 的扩展。有帮助吗? Doh,写下问题的类型混乱。感谢您指出。但问题仍然存在。 这个问题有两种解决方案。 1) 磁铁图案 - 2) 无形。在这种情况下,我个人会选择磁铁,这是最简单的解决方案,其他多个库都使用它来解决同样的问题。 感谢分享。不知道磁铁图案是一回事。一定会看看的。 @Luis 真棒,原来磁铁图案正是我想要的。如果你想整理一个最小的例子,我会接受这个作为答案。否则我将在下周自己为其他人发布答案。 【参考方案1】:

这就是我想出的:

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()
  ),
)

【讨论】:

以上是关于将 varargs 参数限制为某些分离类型的主要内容,如果未能解决你的问题,请参考以下文章

帮介绍一下C语言里的varargs。

Laravel:如何将某些路由参数限制为特定值?

Java Varargs 可变参数使用

Java5,6,7新特性

如何通过预处理主题功能将树枝文件中的块限制为内容类型中的某些页面?

如何通过 dbus 调用 varargs 函数?