簡単な例の答え
(以下の最初の例に対する回答)
それらがすべて同じである限り、 X[_]
inの正確な型パラメーターは気にしないようです。ユーザーがこれを尊重しないcase class Y(xs: X[_]*)
を作成できないようにしたいだけです。Y
これを実現する 1 つの方法は、デフォルトのY
コンストラクターを非公開にすることです。
case class Y private (xs: Seq[X[_]])
// ^^^^^^^ makes the default constructor private to Y, xs is still public
// Note also that xs is now a Seq, we will recover the repeated arg list below.
この方法で独自のコンストラクターを定義します。
object Y {
def apply[B](): Y = Y(Nil)
def apply[B](x0: X[B], xs: X[B]*): Y = Y(x0 +: xs)
// Note that this is equivalent to
// def apply[B](xs: X[B]*): Y = Y(xs)
// but the latter conflicts with the default (now private) constructor
}
今、人は書くことができます
Y()
Y(X("a"))
Y(X(1), X(1), X(5), X(6))
Y[Int](X(1), X(1), X(5), X(6))
そして、以下はコンパイルされません:
Y(X(1), X("1"))
最初の例への答え
上記のように、コンストラクターをプライベートにし、繰り返される引数リストを Seq に変更します。
case class Rect2D[A] private (rows: Seq[Sized[Seq[A], _]])
// ^^^^^^^ ^^^^ ^
独自のコンストラクターを定義しましょう。
object Rect2D {
def apply[A](): Rect2D[A] = Rect2D[A](Nil)
def apply[A,N <: Nat](r0: Sized[Seq[A], N], rs: Sized[Seq[A], N]*): Rect2D[A] = Rect2D[A](r0 +: rs)
}
これで、次のようにコンパイルされます。
val r0: Rect2D[_] = Rect2D()
val r: Rect2D[Int] = Rect2D[Int]()
val r1: Rect2D[Int] = Rect2D(Sized[Seq](1, 2))
val r2: Rect2D[Int] = Rect2D(Sized[Seq](1, 2), Sized[Seq](2, 3))
val r3: Rect2D[Int] = Rect2D(Sized[Seq](1, 2), Sized[Seq](2, 3), Sized[Seq](2, 3), Sized[Seq](2, 3))
val r4: Rect2D[Any] = Rect2D(Sized[Seq](1, 2), Sized[Seq]("a", "b"), Sized[Seq](2, 3), Sized[Seq](2, 3)) // Works because both Sized and Seq are covariant
// Types added as a check, they can be removed
そして、以下はしません:
val r5 = Rect2D(Sized[Seq](1, 2), Sized[Seq](1, 2, 3))
1つの欠点は、次のようなものを書くことができないことです
val r2 = Rect2D[Int](Sized[Seq](1, 2), Sized[Seq](2, 3))
// ^^^^^
代わりにこれを書く必要があります
val r2 = Rect2D[Int, Nat._2](Sized[Seq](1, 2), Sized[Seq](2, 3))
// ^^^^^^^^
これを修正しましょう!
最初の例の拡張ソリューション
よりクリーンなソリューションは、上記のコンストラクターを次のように定義することです。
object Rect2D {
def apply[A,N <: Nat](r0: Sized[Seq[A], N], rs: Sized[Seq[A], N]*): Rect2D[A] = Rect2D[A](r0 +: rs) // Same as above
case class Rect2DBuilder[A]() {
def apply(): Rect2D[A] = Rect2D[A](Nil)
def apply[N <: Nat](r0: Sized[Seq[A], N], rs: Sized[Seq[A], N]*): Rect2D[A] = Rect2D[A](r0 +: rs)
}
def apply[A] = new Rect2DBuilder[A]
}
今、私たちも書くことができます
val r2 = Rect2D[Int](Sized[Seq](1, 2), Sized[Seq](2, 3))
そして、以下はコンパイルされません
val r4 = Rect2D[Int](Sized[Seq](1, 2), Sized[Seq]("a", "b"), Sized[Seq](2, 3), Sized[Seq](2, 3))
// ^^^^^ ^^^^^^^^