Если мы представим columnAttributes
в Column
как HList
, мы в первую очередь необходимо чтобы ограничить элементы HList
подтипами ColumnAttribute
и быть отличными.
Для достижения этой цели мы можем использовать hlist constraintsLUBConstraint
и IsDistinctConstraint
.
import shapeless.{Default => _, _}
import LUBConstraint._
import IsDistinctConstraint._
def acceptOnlyDistinctAttributes
[L <: HList : <<:[ColumnAttribute]#λ : IsDistinctConstraint](l: L): L = l
Теперь нам нужно тягот HList
поэтому он не может содержать как Encode
и DistKey
, к сожалению, мы должны написать этот тип класса сами.
Мы можем использовать =:=
для проверки первого элемента и 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
и не будет содержать не как в Encode
и 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)
Возможно, здесь отсутствует пункт здесь, но добавьте специальный метод подачи для столбца? Подтверждение строительства? –
Ну да, я уже думал об этом. Потому что существует слишком много способов построить недопустимое значение (например, отрицательный «шаг»), даже не принимая во внимание эти множества, поэтому в целом это, вероятно, не стоит. Но вопрос все еще справедлив только ради любопытства. – chuwy
Я не уверен, что вы пытаетесь, но так как вы упоминаете бесформенность и даже приводите пример: HList of Options на самом деле не является справедливым представлением ADT. HLists - это продукты, вам нужен копродукт: https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#coproducts-and-discriminated-unions – pedrofurla