Scala 存在类型与重复参数

Scala 存在类型与重复参数,scala,types,Scala,Types,在Scala中,存在类型作用域是否可能覆盖重复参数的类型 动机 在本例中,我使用以下case类: case class Rect2D[A, N <: Nat](rows: Sized[Seq[A], N]*) 存在式在*下,因此我不能保证所有行都具有相同的第二个类型参数,例如,以下编译,但不应该: Rect2D(Sized(1, 2, 3), Sized(1, 2)) 以下版本具有我想要的语义: case class Rect2D[A](rows: Seq[Sized[Seq[A],

在Scala中,存在类型作用域是否可能覆盖重复参数的类型

动机 在本例中,我使用以下case类:

case class Rect2D[A, N <: Nat](rows: Sized[Seq[A], N]*)
存在式在
*
下,因此我不能保证所有行都具有相同的第二个类型参数,例如,以下编译,但不应该:

Rect2D(Sized(1, 2, 3), Sized(1, 2))
以下版本具有我想要的语义:

case class Rect2D[A](rows: Seq[Sized[Seq[A], N]] forSome { type N <: Nat })
以及:

我不想编译
Y(X(1),X(“1”)
。是的。我知道我可以写:

case class Y(xs: Seq[X[B]] forSome { type B })
或:


但是我想使用重复的参数,不想在
B

上参数化
Y
,举个简单的例子: 您可以在Y上声明其他类型参数:

案例类别Y[V](xs:X[V]*)


此类型参数应该是可推断的,因此从用户角度不需要额外编写。

如果这不违反您的约定,因为您不关心N,您可以利用协方差丢弃存在类型,如下所示:

case class X[A](a: A)
case class Y(xs: X[_]*)
  trait Nat

  trait Sized[A,+B<:Nat]

  object Sized {
    def apply[A,B<:Nat](natSomething:B,items: A *) = new Sized[Seq[A],B] {}
  }

  class NatImpl extends Nat


  case class Rect2D[A](rows:Sized[Seq[A],Nat] * )

  val sizedExample = Sized(new NatImpl,1,2,3)

  Rect2D(Sized(new NatImpl,1,2,3),Sized(new NatImpl,1,2,3),Sized(new NatImpl,1,2,3))
trait-Nat

特征大小[A,+B注:我以前在这里有一个不同的、不起作用的解决方案,但我把它删掉了

编辑:现在是第4版

sealed trait Rect2D[A] extends Product with Serializable { this: Inner[A] =>
  val rows: Seq[Sized[Seq[A], N]] forSome { type N <: Nat }
  def copy(rows: Seq[Sized[Seq[A], N]] forSome { type N <: Nat } = this.rows): Rect2D[A]
}

object Rect2D {
  private[Rect2D] case class Inner[A](rows: Seq[Sized[Seq[A], N]] forSome { type N <: Nat }) extends Rect2D[A]
  def apply[A, N <: Nat](rows: Sized[Seq[A], N]*): Rect2D[A] = Inner[A](rows)
  def unapply[A](r2d: Rect2D[A]): Option[Seq[Sized[Seq[A], N]] forSome { type N <: Nat }] = Inner.unapply(r2d.asInstanceOf[Inner[A]])
}
Rect2D[A]使用可序列化{this:internal[A]=>
val rows:Seq[Sized[Seq[A],N]]对于某些{type N,用于简化示例的答案
(以下第一个例子的答案)

看起来您并不关心
案例类Y(xs:X[\u]*)
X[\u]
的精确类型参数,只要它们都是相同的。您只想阻止用户创建不尊重这一点的
Y

实现这一点的一种方法是将默认的
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
}
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]

}
现在人们可以写作了

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], _]])
//                   ^^^^^^^        ^^^^                ^
让我们定义自己的构造函数:

但以下情况并不适用:

val r5 = Rect2D(Sized[Seq](1, 2), Sized[Seq](1, 2, 3))
一个缺点是你不能写这样的东西

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 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
}
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 r4 = Rect2D[Int](Sized[Seq](1, 2), Sized[Seq]("a", "b"), Sized[Seq](2, 3), Sized[Seq](2, 3))
//             ^^^^^                              ^^^^^^^^

在您的简化示例中,
case class Y[B](xs:X[B]*)
不是一个有效的解决方案吗?@Nicolas:对不起,这个问题没有明确表明我试图避免那个解决方案(这正是我在链接答案中使用的方法)。我已经编辑了以使其更清晰。谢谢,但我确实关心参数,因为我需要它对所有项都相同。编译器可以在
案例类Y(对于某些{type B},xs:Seq[X[B]]中验证这一点)
case,所以我不明白为什么重复参数是不可能的。为了响应您的更新:我可以使用
forSome
将存在的范围提升到外部
Seq
。我想对重复参数版本做同样的操作。
case类Y(xs:X[B]*forSome{type B})
只是没有编译。scala>类X[A]定义类X scala>案例类Y(对于某些{type B}*)定义类Y,xs:X[B]与
版本相同,很不幸。谢谢-我没有想到尝试这个,但遗憾的是,存在主义仍然在
*
Rect2D(大小(1,2),大小(3))
编译,但不应该编译。您的第二个版本有几个问题:第一行中的括号需要移动,并在
N
之前添加
type
,以便语法正常工作。然后我仍然会得到一个“擦除后的相同类型”
apply
错误。但是如果我删除
案例
并使构造函数仅为
私有
,它会提供我想要的用法。因此,到目前为止,这比任何其他方法都更接近,但由于它不适用于案例类,我将在接受之前再坚持一段时间。非常感谢-
val r5 = Rect2D(Sized[Seq](1, 2), Sized[Seq](1, 2, 3))
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))
//             ^^^^^                              ^^^^^^^^