Scala - 在编译时强制执行Vector的大小

时间:2013-02-06 23:45:42

标签: scala tuples compile-time type-systems

是否可以在编译时强制传入方法的Vector的大小?我想使用空间中的一组点来建模一个n维欧几里德空间,看起来像这样(这就是我现在所拥有的):

case class EuclideanPoint(coordinates: Vector[Double]) {
  def distanceTo(desination: EuclieanPoint): Double = ???
}

如果我有一个通过EuclideanPoint(Vector(1, 0, 0))创建的坐标,那么它就是一个3D欧几里德点。鉴于此,我想确保调用distanceTo时传递的目标点具有相同的维度。

我知道我可以通过使用Tuple1Tuple22来实现这一点,但是我希望代表许多不同的几何空间,如果我用{{1}我会为每个空间编写22个类s - 有更好的方法吗?

2 个答案:

答案 0 :(得分:12)

可以通过多种方式实现这一点,所有这些方式或多或少都与Randall Schulz在评论中描述的方式相似。 Shapeless library提供了一个特别方便的实现,它可以让你得到一些非常接近你想要的东西:

import shapeless._

case class EuclideanPoint[N <: Nat](
   coordinates: Sized[IndexedSeq[Double], N] { type A = Double }
) {
  def distanceTo(destination: EuclideanPoint[N]): Double = 
    math.sqrt(
      (this.coordinates zip destination.coordinates).map {
        case (a, b) => (a - b) * (a - b)
      }.sum
    )
}

现在您可以写下以下内容:

val orig2d = EuclideanPoint(Sized(0.0, 0.0))
val unit2d = EuclideanPoint(Sized(1.0, 1.0))

val orig3d = EuclideanPoint(Sized(0.0, 0.0, 0.0))
val unit3d = EuclideanPoint(Sized(1.0, 1.0, 1.0))

scala> orig2d distanceTo unit2d
res0: Double = 1.4142135623730951

scala> orig3d distanceTo unit3d
res1: Double = 1.7320508075688772

但不是:

scala> orig2d distanceTo unit3d
<console>:15: error: type mismatch;
 found   : EuclideanPoint[shapeless.Nat._3]
 required: EuclideanPoint[shapeless.Nat._2]
              orig2d distanceTo unit3d
                                ^

Sized附带了许多不错的功能,包括一些带有长度静态保证的集合操作。我们可以编写以下代码:

val somewhere = EuclideanPoint(Sized(0.0) ++ Sized(1.0, 0.0))

在三维空间中有一个普通的老点。

答案 1 :(得分:2)

您可以通过对自然数字进行类型级别编码来完成自己的工作,例如:http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/。然后只需通过自然参数化Vector。不需要额外的依赖,但可能比使用Shapeless更复杂。