views:

117

answers:

2

Why does the call to fn(Iterator("foo") compile, but the call to fn(fooIterator) fail with an error "type mismatch; found : Iterator[java.lang.String] required: scala.Iterator[com.banshee.Qx.HasLength]"

object Qx {
    type HasLength = {def length: Int}
    def fn(xs: Iterator[HasLength]) = 3
    var tn = fn(Iterator("foo"))
    var fooIterator = Iterator("foo")
    var tnFails = fn(fooIterator) //doesn't compile
}

Aren't they the same thing?

+3  A: 

This formulation works:

object Qx {
    type HasLength = {def length: Int}
    def fn[HL <% HasLength](xs: Iterator[HL]) = 3
    val tn = fn(Iterator("foo"))
    val fooIterator = Iterator("foo")
    val tnFails = fn(fooIterator)
}
Randall Schulz
I'd love a unified explanation of this comment and extempore's comment about zero-argument-length-vs-length-with-empty-parens. I have a feeling there's something subtle going on here that I'm not understanding.
James Moore
+3  A: 

It has to be a bug in the representation of refinements, because the following two formulations both work.

object Qx1 {
    // give length() parens, even though the Iterator definition doesn't have them
    type HasLength = { def length(): Int }

    def fn(xs: Iterator[HasLength]) = 3
    var tn = fn(Iterator("foo"))
    var fooIterator = Iterator("foo")
    var tnFails = fn(fooIterator) //doesn't compile
}

object Qx2 {
    type HasLength = { def length: Int }

    def fn(xs: Iterator[HasLength]) = 3
    var tn = fn(Iterator("foo"))
    // annotate the type of fooIterator before the type inferencer can mis-infer
    var fooIterator: Iterator[HasLength] = Iterator("foo")
    var tnFails = fn(fooIterator) //doesn't compile
}

Edit:

Too early in the morning. It's String with the length() method, which does have parens, which means it's right and you're wrong for thinking length and length() are the same method. (It's a nice little trap I've documented before.)

extempore
If it's just length v. length(), shouldn't his first example fail as well?var tn = fn(Iterator("foo"))
Adam Rabung
And why does Randall Schulz's version work with the view projection? I'm a bit baffled.
James Moore
It is just length vs. length. fn(Iterator("foo")) works for the same reason annotating the type of fooIterator works: the expected type of the expression influences the inferred type. In the failing example an incompatible type is inferred in the declaration, and then the call is made in a separate statement.
extempore