使用Shapeless实现编译安全索引

时间:2018-01-18 19:22:06

标签: scala types shapeless

问题: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来实现优雅的解决方案。

0 个答案:

没有答案