Patrik Patrik - 3 months ago 6
Scala Question

Scala higher-kinded types general factory

I'v been playing around with scala again testing some language type check features. I'm trying to implement a vector library intened for use with graphics and I want to use the scala typechecker as much as possible to get early compile time warnings when using it. What I'v got so far.

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)


this allows me to do compile time checking for operations like even if the
+
operator is implemented in the
abstract
class

(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


which is what i want.

So far so good. Now i would like to have a map function implemented in the abstract class something like

def map[A](f: T => A): V[A] = Vec(elems.map(f):_*)


So my attempt of realizing this would be to create a general Vec factory

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


but that wont work,
expression of type V does not comform to exptected type V[T]


So my question. Is there any "good" general way of implementing a factory like this?

Answer

For why new V[T](...) doesn't and shouldn't work, see http://stackoverflow.com/a/39286308/9204. One solution would be

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] = ...
}

Note that this still isn't perfectly safe: the factories need to be called with sequences of specific length.