【问题标题】:Type-level Set for Algebraic Data Type代数数据类型的类型级集
【发布时间】:2016-06-22 15:44:20
【问题描述】:

这是这个问题的部分重复:Getting subclasses of a sealed trait,但答案建议运行时反射,这对我来说不合适,我想知道它是否可以在编译时使用,可能使用 Shapeless。

所以,有了这个 ADT:

sealed trait ColumnAttribute
case class Default(value: String) extends ColumnAttribute
case class Identity(seed: Int, step: Int) extends ColumnAttribute
case class Encode(encoding: CompressionEncoding) extends ColumnAttribute
case object DistKey extends ColumnAttribute

我怎样才能得到像Option[Default] :: Option[Identity] :: Option[Encode] :: Option[DistKey] :: HNil 这样的东西?

更具体的问题(可能我正在寻找错误的解决方案)。 拥有以上 AST 和以下类,我怎么能确定在编译时 Column 不会被多个 EncodeDistKey 或其他 ColumnAttribute 构造。

case class Column(columnName: String, dataType: DataType, columnAttributes: Set[ColumnAttribute], columnConstraints: Set[ColumnConstraint])

UPDcolumnAttributes 应该只包含一个特定子类型的值,但可以包含多个不同子类型的值。

所以,这个伪代码应该是正确的:

columnConstraint = Default("foo") :: DistKey :: Identity(1,2) :: HNil

但这应该会失败:

columnConstraint = Default("foo") :: Default("bar") :: HNil

【问题讨论】:

  • 这里可能漏掉了重点,但是为 Column 添加了一个自定义的应用方法?验证施工?
  • 好吧,我已经考虑过了。因为有太多方法可以构造无效值(例如负数step),甚至不考虑这些集合,所以总体而言它可能不值得。但是出于好奇,问题仍然有效。
  • 我不确定你在尝试什么,但既然你提到了 shapeless 甚至提供了一个例子:选项的 HList 并不是 ADT 的真正公平表示。 HLists 是产品,你需要一个副产品:github.com/milessabin/shapeless/wiki/…
  • 嘿@pedrofurla!对我来说,它看起来更像是一种产品。因为 coproduct 假设我们有一个值是 DistKey or Encode or 另一个 ColumnAttribute,但我需要的是可选 @987654337 的(可能是空的)乘积@可选Encode

标签: scala abstract-syntax-tree shapeless type-level-computation


【解决方案1】:

如果我们将Column 中的columnAttributes 表示为HList,我们首先需要将HList 元素约束为ColumnAttribute 的子类型并且是不同的。

我们可以使用hlist constraintsLUBConstraintIsDistinctConstraint来实现这一点。

import shapeless.{Default => _, _}
import LUBConstraint._
import IsDistinctConstraint._

def acceptOnlyDistinctAttributes
  [L <: HList : <<:[ColumnAttribute]#λ : IsDistinctConstraint](l: L): L = l

现在我们需要约束HList,所以它不能同时包含EncodeDistKey,不幸的是我们需要自己编写这个类型类。

我们可以使用=:=检查第一个元素,使用NotContainsConstraint检查尾部是否不包含其他类型。

trait OnlyOneOfConstraint[L <: HList, A, B] extends Serializable

object OnlyOneOfConstraint {

  def apply[L <: HList, A, B]
    (implicit ooo: OnlyOneOfConstraint[L, A, B]): OnlyOneOfConstraint[L, A, B] = ooo

  type OnlyOneOf[A, B] = {
    type λ[L <: HList] = OnlyOneOfConstraint[L, A, B]
  }

  implicit def hnilOnlyOneOf[A, B] = new OnlyOneOfConstraint[HNil, A, B] {}

  // head is A, so tail cannot contain B
  implicit def hlistOnlyOneOfA[H, T <: HList, A, B](implicit
    ncB: T NotContainsConstraint B, 
    eq: A =:= H,
    oooT: OnlyOneOfConstraint[T, A, B]
  ) = new OnlyOneOfConstraint[H :: T, A, B] {}

  // head is B, so tail cannot contain A
  implicit def hlistOnlyOneOfB[H, T <: HList, A, B](implicit
    ncA: T NotContainsConstraint A, 
    eq: B =:= H,
    oooT: OnlyOneOfConstraint[T, A, B]
  ) = new OnlyOneOfConstraint[H :: T, A, B] {}

  // head is not A or B
  implicit def hlistOnlyOneOf[H, T <: HList, A, B](implicit
    neqA: A =:!= H,
    neqB: B =:!= H,
    oooT: OnlyOneOfConstraint[T, A, B]
  ) = new OnlyOneOfConstraint[H :: T, A, B] {}
}

现在我们可以使用这些约束编写(简化的)Column

type CompressionEncoding = String

sealed trait ColumnAttribute
case class Default(value: String) extends ColumnAttribute
case class Identity(seed: Int, step: Int) extends ColumnAttribute
case class Encode(encoding: CompressionEncoding) extends ColumnAttribute
case object DistKey extends ColumnAttribute

import OnlyOneOfConstraint._

case class Column[
  Attrs <: HList 
    : <<:[ColumnAttribute]#λ 
    : IsDistinctConstraint 
    : OnlyOneOf[Encode, DistKey.type]#λ
](columnAttributes: Attrs)

我们现在有一个编译时保证属性是不同的ColumnAttributes,并且不会同时包含EncodeDistKey

Column(DistKey :: Default("s") :: HNil)
// Column[shapeless.::[DistKey.type,shapeless.::[Default,shapeless.HNil]]] = Column(DistKey :: Default(s) :: HNil)

Column(Default("s") :: Encode("a") :: HNil)
// Column[shapeless.::[Default,shapeless.::[Encode,shapeless.HNil]]] = Column(Default(s) :: Encode(a) :: HNil)

Column(DistKey :: Default("s") :: Encode("a") :: HNil)
// <console>:93: error: could not find implicit value for evidence parameter of type OnlyOneOfConstraint[shapeless.::[DistKey.type,shapeless.::[Default,shapeless.::[Encode,shapeless.HNil]]],Encode,DistKey.type]
//        Column(DistKey :: Default("s") :: Encode("a") :: HNil)

【讨论】:

  • 谢谢@PeterNeyens。这部分回答了我的问题。部分原因是我似乎表述得不够清楚(更新的主题)。但它满足了我的好奇心,看起来就像是无形中的绝妙碎片。
  • 根据您更新的问题,您似乎不需要OnlyOneOf 约束,只需要LUBIsDistinct
猜你喜欢
  • 1970-01-01
  • 2011-06-22
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多