Scala - Enforcing size of Vector at compile time

后端 未结 2 1088
不思量自难忘°
不思量自难忘° 2021-01-02 02:58

Is it possible to enforce the size of a Vector passed in to a method at compile time? I want to model an n-dimensional Euclidean space using a collection of poi

2条回答
  •  醉梦人生
    2021-01-02 03:35

    It is possible to do this in a number of ways that all look more or less like what Randall Schulz has described in a comment. The Shapeless library provides a particularly convenient implementation, which lets you get something pretty close to what you want like this:

    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
        )
    }
    

    Now you can write the following:

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

    And:

    scala> orig2d distanceTo unit2d
    res0: Double = 1.4142135623730951
    
    scala> orig3d distanceTo unit3d
    res1: Double = 1.7320508075688772
    

    But not:

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

    Sized comes with a number of nice features, including a handful of collections operations that carry along static guarantees about length. We can write the following for example:

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

    And have an ordinary old point in three-dimensional space.

提交回复
热议问题