Reputation: 23788
I'm playing with (path-)dependent types in Scala and stumbled upon a following scenario that I can't find a good solution for. Assume that I want to have some hierarchy of dependent types and I want each of them to have a reference back to its "owner" object. I want this back-reference to be able to call some methods on the correct "owner" objects. What is the correct way to do it?
Here is a small example. There is a "base" trait Outer
with a dependent type Inner
. The base Outer
trait defines some method double
that works on the dependent type. There is also a specific class ConcreteOuter
with a specific dependent class ConcreteInner
that uses simple Int
for the value.
trait Outer {
outerSelf =>
trait BaseInner {
val outer: outerSelf.type = outerSelf
def asDependent: outer.Inner // #1
// def asDependent: outerSelf.Inner // #2
}
type Inner <: BaseInner
def double(inner: Inner): Inner
}
class ConcreteOuter extends Outer {
case class ConcreteInner(val v: Int) extends BaseInner {
override def asDependent = this
}
type Inner = ConcreteInner
def createInner(v: Int): Inner = new ConcreteInner(v)
override def double(inner: Inner): Inner = new ConcreteInner(2 * inner.v)
}
So far so good. Now assume I'd like to be able to call that double
method in a context where I have only an instance of some Inner
class but not the corresponding Outer
-instance. For example, let's try to create another double
method that just calls the original Outer.double
in some other (independent) context:
object DepTest extends App {
//def double(inner: Outer#Inner) = inner.outer.double(inner) // #3
def double(inner: Outer#Inner) = inner.outer.double(inner.asDependent) // #4
val c1 = new ConcreteOuter
val i1 = c1.createInner(123)
val d1 = double(i1)
println(d1)
}
This code compiles but requires a quite ugly hack of asDependent
. If I use line #3 instead of line #4, the code doesn't compile. If I split the line #3 in a following way the code doesn't compile anymore
def double(inner: Outer#Inner) = {
val outer = inner.outer
outer.double(inner.asDependent)
}
Moreover, if I replace line #1 with line #2 even the asDependent
hack stops working.
So it looks like somehow sometimes the compiler knows that the outer
field of the Inner
object and the "owner" object aka outerSelf
is the same thing and sometimes it doesn't and it is not clear how to persuade the compiler when it doesn't recognize them as the same thing.
Is there a way to work this around? Or is this a totally wrong approach to my problem? (Obviously in the real world I'd like to create not just dumb proxies such as DepTest.double
but some library of higher level functions such as multiplyByPow2(val : Outer#Inner, exponent: Int)
)
Upvotes: 1
Views: 125
Reputation: 392
I am not very experienced with path-dependant types, but from what I can read from here, it seems that what happens is that Outer
and outerSelf.type
are not the same thing:
outerSelf.type
consists of either outerSelf
or null
outerSelf: Outer
consists of only outerSelf
I think your problem is coming from here, but I have not enough knowledge on this to be able to help you more.
Upvotes: 0