2

这是这个问题的部分重复:Getting subclasses of a sealed trait,但答案建议运行时反射这是不适合我,我想知道是否可能在编译时,可能使用无形。代数数据类型的类型级集合

所以,有这个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

+0

可能会错过这里的意思,但为列添加自定义应用方法?验证施工? –

+0

嗯,我已经考虑过了。因为构建无效值的方法太多了(即不考虑这些集合,所以无法构造无效的值),因此总体而言可能不值得。只是出于好奇而问题仍然有效。 – chuwy

+3

我不确定你在尝试什么,但是因为你提到没有形状,甚至提供了一个例子:选项的HList并不是真正的ADT的公平表示。 HLists是产品,你需要一个副产品:https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#coproducts-and-discriminated-unions – pedrofurla

回答

3

如果我们Column代表columnAttributesHList,我们首先需要以限制HList元素为ColumnAttribute的子类型并且是不同的。

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

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

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

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

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

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并不会不能同时包含Encode和:

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这些约束写DistKey

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) 
+0

谢谢@PeterNeyens。这部分回答了我的问题。部分原因是因为我似乎没有足够清楚地阐述它(更新的主题)。但它满足了我的好奇心,看起来像是无形的废品。 – chuwy

+1

随着您的更新问题,似乎你不需要'OnlyOneOf'约束,只需要'LUB'和'IsDistinct'。 –