Confusion with path projections

64 views Asked by At

Here is what I am trying to do

class Bar[P](val x: P) { type Foo = P }
class Baz[R <: Bar[_]](bar: R) { val x: R#Foo = bar.x }

The idea is to create Baz with a single type parameter, and have access to both types inside of it.

But this does not work :(

found   : Baz.this.x.type (with underlying type _$1)
required: _$1

It sounds like "underlying type _$1" is exactly what I want here, but that does not compile. Is there a way to do what I want here?

update

Perhaps, I oversimplified the use case. Let's say, Bar is actually something like this:

trait Foo[T] { def doStuff(t: T) = ??? }
class Bar[P] extends Foo[P](val x: P) { type Foo = P }

and somewhere else I have a

 def fooX[T](x: T, foo: Foo[T]) = foo.doStuff(x)

and I want to call it from Baz:

 class Baz[R <: Bar[_]](bar: R) { fooX(bar.x, bar) }

It feels like it should work: fooX parameters will always be of the correct type. But it does not compile, and I can't think of a better workaround than to have two type parameters to Baz, which seems redundant.

1

There are 1 answers

1
Andrey Tyukin On BEST ANSWER

When you invoke fooX(bar.x, bar), the compiler sees only:

  • bar: Bar[_] which is the same as bar : Bar[X] forSome { type X }
  • bar.x : X forSome { type X }, which reduces to bar.x: Any

and then it fails to prove that Any is the same as X in forSome { type X }. However, if you bind the unknown type to a variable p, then the compiler sees:

  • bar: Bar[p]
  • bar.x : p
  • fooX[p](bar.x, bar) is applicable.

Therefore, this works:

trait Foo[T] { def doStuff(t: T) = ??? }
class Bar[P](val x: P) extends Foo[P] { type Foo = P }

def fooX[T](x: T, foo: Foo[T]) = foo.doStuff(x)

class Baz[R <: Bar[_]](bar: R){
  bar match {
    case b: Bar[p] => fooX[p](b.x, b)
  }
}

Outtakes (pre-update attempts)

Two snippets that attempted to solve the original question, maybe you find something helpful here:

class Bar[P](val x: P) { type Foo = P ; val y: Foo = x }
class Baz[R <: Bar[_]](val bar: R) { val x: bar.Foo = bar.y }

another way to tie x to Foo:

trait Br { type Foo ; val x: Foo }
class Bar[F](val x: F) extends Br { type Foo = F }
class Baz[R <: Br](val bar: R) {val x: bar.Foo = bar.x }