John Stanford
John Stanford

Reputation: 1093

Use a Scala Trait to Mark Instances of a Class

I have a case class that represents 3D vectors and I'm trying to use traits to mark each one with reference frames relevant to each problem domain. More specifically, I'm trying to do something like this:

trait bFrame
type bVector = Vector with bFrame

/** Inertial position of the point represented in b+ */
def p_b:bVector = Vector(x + r * sP, y - cP*sR*r, z - cP*cR*r) with bFrame

The expressions in the constructor evaluate to doubles and everything works fine before I try this trait trick. I've read that you can apply traits to instances of classes and not just classes themselves, but it doesn't seem to work here. The error I get is "';' expected but 'with' found." I want to use the type system to check reference frames without having to modify the original class. Is there a way to make this work?

Upvotes: 0

Views: 158

Answers (2)

Evgeny
Evgeny

Reputation: 1770

Not enough space in comment to answer to

It doesn't look like it creates a new anonymous class. When I add ....

It is =)

Example:

$ cat T.scala
trait A
case class T(name: String)
object B extends App {
  val a = new T("123") with A
  println(a)
}
$ scalac -Xprint:typer T.scala

I skip most of output - you can check it by yourself. Most interesting:

...
private[this] val a: T with A = {
  final class $anon extends T with A {
    def <init>(): <$anon: T with A> = {
      $anon.super.<init>("123");
      ()
    }
  };
  new $anon()
};
<stable> <accessor> def a: T with A = B.this.a;
...

as you can see - anonymous class initialization.

Upvotes: 1

John Stanford
John Stanford

Reputation: 1093

I think I figured it out. It's not clear why, but for some reason Scala (as of 2.12.2) doesn't like for you to use the "apply" way of constructing case classes. I have to add "new" to make it work. Also, I should have been more clear originally that Vector is a case class that represents a vector in the mathematical sense, not the Scala collection. I changed it to Vector3D here to make it clearer. Also, the 2.12.2 compiler says that the line that prints "Vector in the b-Frame" is unreachable, but then when I run it, that line gets executed (i.e. the output is what you'd expect). Maybe this is a bug in the compiler. I'll try it with a more recent version of Scala and see.

object Test extends App {

  case class Vector3D(x:Double, y:Double, z:Double)

  trait bFrame
  trait nFrame
  type bVector3D = Vector3D with bFrame
  type nVector3D = Vector3D with nFrame

  val p_b:bVector3D = new Vector3D(1.0, 2.0, 3.0) with bFrame  //Works
  //val p_b:bVector3D = Vector3D(1.0, 2.0, 3.0) with bFrame    //Doesn't work

  p_b match {
    case _:nVector3D => println("Vector in the n-Frame")
    case _:bVector3D => println("Vector in the b-Frame")      //Compiler says this is unreachable
    case _:Vector3D  => println("Vector in an undetermined frame")
    case _           => println("Something other than a vector")
  }
}

Upvotes: 0

Related Questions