问题:让val v = List(0.5, 1.2, 0.3)
模拟某个向量的实现 v =(v_1,v_2,v_3) 。 v_j 中的索引 j 由元素在列表中的位置隐含。许多样板和错误都是由于跟踪这些索引,例如,在从原始列表创建修改后的列表时。 (How to make sure (in compile-time) that collection wasn't reordered?似乎有关。)
一般问题:什么是在编译时确保正确索引的好方法? (我认为表现并不重要。)
我的计划是在无形状中使用Nat
的子类来将索引建模为(显式)类型。目前的解决方案是
import shapeless._
import Nat._
trait Elem[+A, +N<:Nat]{
val v: A
val ind: N}
case class DecElem[N<:Nat](v: BigDecimal, ind: N) extends Elem[BigDecimal, N]
object Decimals {
type One = DecElem[ _0]:: HNil
type Two = DecElem[ _0]:: DecElem[_1] :: HNil
//...
}
case class Scalar(v: Decimals.One)
case class VecTwo(v: Decimals.Two)
然而,这在更大的方面变得乏味。
另一个问题是如何在trait Elem[+A, +N<:Nat]
中处理通用案例。首先,我定义了case class ElemVector[A, M<:Nat](vs: Sized[List[Elem[A, Nat]], M])
,它在Elem
中丢失了特定的索引类型。什么可能是规避这种困难的策略?
(注意:一个更好的设计可能是通过附加显式索引并处理包装类来包装List[A]
。但是,这并没有实质上改变问题。)
更新这里是矢量元素意外交换的简单说明。
import shapeless.Nat._
import shapeless.{Sized, nat}
type VectorTwo = Sized[IndexedSeq[Double], nat._2]
val f = (p: VectorTwo, x: Double) => {
val V = p(_0)
val K = p(_1)
V * x/(K + x)
}
val V : Double = 500
val K : Double = 1
val correct: VectorTwo = Sized(V, K)
val wrong: VectorTwo = Sized(K, V) //Compiles!
f(correct, 10) // = 454.54
f(wrong, 10) // = 0.02
我想让编译器在具有许多元素的向量中防止出现此类错误,并想知道是否可以使用Shapeless来实现优雅的解决方案。