5

Normally, when collecting all the elements of a sequence that match a particular type, the resulting collection has both the type of the original collection and the type selected for:

trait Foo
trait Bar

trait Baz {
  // Works
  def def1(foo: Seq[Foo]): Seq[Foo with Bar] = 
    foo collect {case foobar: Bar => foobar}
}

This even works when the the input type is parameterized by a bounded member type and all I want back is a sequence parameterized by the bound type (not the member type):

trait Baz {
  type memberType <: Foo

  // Works
  def2(foo: Seq[memberType]): Seq[Foo with Bar] =
    foo collect {case foobar: Bar => foobar}
}

However, this fails when I actually want back a sequence parameterized by the member type:

trait Baz {
  type memberType <: Foo

  // Fails
  def def3(foo: Seq[memberType]): Seq[memberType with Bar] =
    foo collect {case foobar: Bar => foobar}
}

Error message:

error: type mismatch;
 found   : Seq[this.Foo with this.Bar]
 required: Seq[Baz.this.memberType with this.Bar]
    foo collect {case foobar: Bar => foobar}

To recover functionality, I can include the member type in the collect call, but that seems redundant given that every element has to match that type because of the signature:

trait Baz {
  type memberType <: Foo

  // Works
  def def4(foo: Seq[memberType]): Seq[memberType with Bar] =
    foo collect {case foobar: memberType with Bar => foobar}
}

Is there a way to define a sequence of member types so that they remember their member types when collected?

0__
  • 66,707
  • 21
  • 171
  • 266
drhagen
  • 8,331
  • 8
  • 53
  • 82
  • 1
    Have checked that code for warnings? Since `memberType` is abstract, it is erased in the `foobar: memberType with Bar` check. – Daniel C. Sobral Jun 27 '12 at 15:44
  • I'm slightly confused why 1 and 2 even work. The collected sequence is just a Seq[Bar] but the compiler still allows it to be typed as a Seq[Foo with Bar]. Okay I guess I'm more confused why this doesn't result in an erasure warning. – Kaito Jun 27 '12 at 17:19
  • I see no warnings when I run any of the working examples. Would type erasure have any effect on the last example (which has been edited to correctly indicate that it "Works")? – drhagen Jun 27 '12 at 17:26
  • 1
    That's quite odd but it does show the warnings if you turn Baz into a class. – Kaito Jun 27 '12 at 17:50

1 Answers1

1

This is not answer, but just some observation. This works:

trait Baz[A <: Foo] {
  def def3(foo: Seq[A]): Seq[A] =
    foo collect {case foobar => foobar}
}

but this not:

trait Baz[A <: Foo] {
  def def3(foo: Seq[A]): Seq[A] =
    foo collect {case foobar: Bar => foobar}
}

also this not:

trait Baz[A] {
  def def3(foo: Seq[A]): Seq[A] =
    foo collect {case foobar: Bar => foobar}
}

so it seems to be an effect of the pattern matcher; I don't think it has anything to do with collect in particular. Somehow it looses information as soon as you add the type to the match case :-/

But as Kaito, I was actually surprised, too, that your first two cases did work. It must be (and seems reasonable) that the type inference algorithm makes a difference between a known stable type (Foo) and a type parameter or type member. You might need to ask on the scala language mailing list to get some of the compiler gurus to answer the question...

0__
  • 66,707
  • 21
  • 171
  • 266