我一直在玩scala再次测试一些语言类型检查功能。我试图实现一个用于图形的矢量库,我想尽可能多地使用scala类型检查器来获得使用它时的早期编译时警告。到目前为止我得到了什么。
trait VecT
abstract sealed class Vec[T,V[T] <: VecT](elems: T*)(implicit num: VecIntegral[T]) extends VecT {
import num._
def +(v: V[T]): V[T] = ???
def -(v: V[T]): V[T] = ???
def cross(v: V[T]): V[T] = ???
def dot(v: V[T]): T = ???
def unary_-(): V[T] = ???
def *(scalar: T): V[T] = ???
def abs: T = ???
def *(v: V[T]): V[T] = cross(v)
def apply(n: Int): T = ???
}
class Vec2[T](x: T, y: T)(implicit num: VecIntegral[T]) extends Vec[T, Vec2](x,y)
class Vec3[T](x: T, y: T, z: T)(implicit num: VecIntegral[T]) extends Vec[T, Vec3](x,y,z)
这允许我对运算进行编译时检查,即使在+
类中实现了abstract
运算符
(new Vec3[Int](1,2,3)) + (new Vec3[Int](1,2,3))
(new Vec3[Int](1,2,3)) + (new Vec2[Int](1,2)) // Failes compilation
这就是我想要的。
到目前为止一切顺利。现在我希望在抽象类中实现一个类似
的map函数def map[A](f: T => A): V[A] = Vec(elems.map(f):_*)
因此,我实现这一目标的尝试将是创建一个普通的Vec工厂
object Vec {
def apply[T, V[T] <: VecT](elemes: T*)(implicit num: VecIntegral[T]): V[T] = elems match {
case Seq(x,y) => new V[T](x,y)
case Seq(x,y,z) => new V[T](x,y,z)
}
但那不起作用,expression of type V does not comform to exptected type V[T]
所以我的问题。有没有&#34;好&#34;这样实施工厂的一般方式是什么?
答案 0 :(得分:1)
为什么new V[T](...)
没有也不应该有效,请参阅https://stackoverflow.com/a/39286308/9204。一种解决方案是
trait VecFactory[V[_]] { def newVec[T](elems: Seq[T]): V[T] }
abstract sealed class Vec[T,V[T] <: VecT](elems: T*)(implicit num: VecIntegral[T], factory: VecFactory[V]) {
def map[A](f: T => A): V[A] = factory.newVec(elems.map(f))
}
class Vec2[T](x: T, y: T)(implicit num: VecIntegral[T]) extends Vec[T, Vec2](x,y)
object Vec2 {
implicit val factory: VecFactory[Vec2] = new VecFactory[Vec2] {
def newVec[T](elems: Seq[T]) = new Vec2(elems(0), elems(1))
}
}
class Vec3[T](x: T, y: T, z: T)(implicit num: VecIntegral[T]) extends Vec[T, Vec3](x,y,z)
object Vec3 {
implicit val factory: VecFactory[Vec3] = ...
}
请注意,这仍然不是完全安全的:需要使用特定长度的序列调用工厂。