Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid awful type casts working with path dependent types?

I am new to Scala and dont know why i have to do an (unintuitive for me) type cast related to path dependent types in the following code. (I don't like getters, setters nor nulls, they are here to separate operations and disambiguate the source of errors)

// Module A public API
class ModA {
  trait A
}

// Module B public API that depends on types defined in Module A
class ModB(val modA: ModA) {
  trait B {
    def getA: modA.A;
    def setA(anA: modA.A);
  }
}

// One implementation of Module A
class ModAImpl extends ModA {
  class AImpl extends A
}

// One implementation of Module B
class ModBImpl(mod: ModA) extends ModB(mod) {
  class BImpl extends B {
    private[this] var privA: modA.A = _;
    override def getA = privA;
    override def setA(anA: modA.A) = privA = anA;
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    // wiring the modules
    val modAImpl = new ModAImpl;
    val modBImpl = new ModBImpl(modAImpl);

    // wiring objects
    val a = new modAImpl.AImpl;
    val b = new modBImpl.BImpl;
    b.setA(a); //don't compile and complain: type mismatch;  found: modAImpl.A  required: modBImpl.modA.A

    //i have to do this horrible and coutnerintuitive cast to workaround it
    b.setA(a.asInstanceOf[modBImpl.modA.A]);

    var someA: modAImpl.A = null;
    someA = b.getA; // don't compile with same reason
    someA = b.getA.asInstanceOf[modAImpl.A]; // horrible cast to workaround

    println(a == b.getA); // however this prints true
    println(a eq b.getA); // this prints true too
  }
} 

I have read about singleton types to inform the compiler when two types are the same, but I don't know how to apply this here. Thanks in advance.

like image 695
Guillermo Pollitzer Avatar asked Nov 11 '12 00:11

Guillermo Pollitzer


1 Answers

You can stick a type parameter on the ModB types:

class ModA { trait A }

class ModB[AA](val modA: ModA { type A = AA }) {
  trait B {
    def getA: AA
    def setA(anA: AA)
  }
}

class ModAImpl extends ModA { class AImpl extends A }

class ModBImpl[AA](
  mod: ModA { type A = AA }) extends ModB(mod) {
  class BImpl extends B {
    private[this] var privA: AA = _
    override def getA = privA
    override def setA(anA: AA) = privA = anA
  }
}

And the type inference all works out as desired:

scala> val modAImpl = new ModAImpl
modAImpl: ModAImpl = ModAImpl@7139edf6

scala> val modBImpl = new ModBImpl(modAImpl)
modBImpl: ModBImpl[modAImpl.A] = ModBImpl@1dd7b098

scala> val a = new modAImpl.AImpl
a: modAImpl.AImpl = ModAImpl$AImpl@4cbde97a

scala> val b = new modBImpl.BImpl
b: modBImpl.BImpl = ModBImpl$BImpl@63dfafd6

scala> b.setA(a)
like image 132
Travis Brown Avatar answered Oct 11 '22 15:10

Travis Brown