This page is no longer maintained — Please continue to the home page at www.scala-lang.org

isInstanceOf vs. type matching: round 2

2 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

I just checked in some light reading for language lawyers. Solicit
feedback, anything I missed, anything I have wrong.

http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk/test/pending/run/inst...

Required background:

https://lampsvn.epfl.ch/trac/scala/ticket/1683
https://lampsvn.epfl.ch/trac/scala/ticket/1698

I enclose the summary in this email, see the first link for lots more.
All I will add is the motivation. Look at the source to Iterator's
duplicate method. I wanted to add an equals method which would return
true if the instances being compared were created on the same method
invocation (i.e. they are truly partners) and there was no gap. However
there is I think no way to perform this test! All variations on type
testing return true.

In that specific example I could test something else, like reference
equality on the "gap" Queue which they both close over, but that's a
workaround, not a resolution.

object Summary {
class Outer {
class Inner { }
def f() = { class MethodInner ; new MethodInner }
}

// 1 static issue:
//
// Given method in MethodInner: def g(other: MethodInner) = ()
// method1.g(method1) fails to compile with type error.
//
// Note that this cannot be worked around by widening the return type
// of f() because MethodInner is declared inside of f. So there is no way
// I see for a class declared inside a method to receive members of its
// own declared type -- not only the narrow type of those from this
// instance, but ANY members, because there is no Foo#Bar syntax which will
// traverse a method.
//
// 4 runtime issues:
//
// From the outside: inner1.isInstanceOf[outer2.Inner] is true, should (maybe) be false
// From inside inner1: inner2.isInstanceOf[Outer.this.Inner] is true, should (maybe) be false
// From the outside: inner1 match { case _: outer2.Inner => true ... } is true, should definitely be false
// From inside method1: method2 match { case _: MethodInner => true ... } is true, should definitely be false
//
// Note that the fact that every test returns true on instances of MethodInner means
// that it is impossible to draw any type distinction between instances. As far as one
// can tell, they are all of the same type regardless not only of whether they were
// created on the same method invocation but whether they are contained in the same
// instance of Outer.
//
// WRT "same method invocation", see Iterator.duplicate for an example of this.
}

Mark Harrah
Joined: 2008-12-18,
User offline. Last seen 35 weeks 3 days ago.
Re: isInstanceOf vs. type matching: round 2

Quoting Paul Phillips :

> I just checked in some light reading for language lawyers. Solicit
> feedback, anything I missed, anything I have wrong.
>
> http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk/test/pending/run/inst...
>
> Required background:
>
> https://lampsvn.epfl.ch/trac/scala/ticket/1683
> https://lampsvn.epfl.ch/trac/scala/ticket/1698
>
> I enclose the summary in this email, see the first link for lots more.
> All I will add is the motivation. Look at the source to Iterator's
> duplicate method. I wanted to add an equals method which would return
> true if the instances being compared were created on the same method
> invocation (i.e. they are truly partners) and there was no gap. However
> there is I think no way to perform this test! All variations on type
> testing return true.

Given section 6.11 of the specification, the behavior in runtime issue
#4 seems reasonable to me. There is no proper supertype of MethodInner
that is a nested class, so MethodInner is not treated as a nested class.

I agree that it would make more sense for the other three examples to
be false. I don't mind so much what isInstanceOf does, but I agree
that pattern matching should do it right if possible.

> In that specific example I could test something else, like reference
> equality on the "gap" Queue which they both close over, but that's a
> workaround, not a resolution.

In the context of 6.11, this looks like a good technique to get
equality the way you want rather than a workaround. It doesn't seem
that bad to use a dummy object in the general case either.

As for the static issue...

Defining MethodInner to have a method that accepts a parameter of type
MethodInner is interesting. The resulting type outside the block is,
to my knowledge, not a valid type in Scala- scalac says

"Parameter type in structural refinement may not refer to an abstract
type defined outside that refinement"

when trying to define a similar existential type directly:

type MI = MethodInner forSome { type MethodInner <: java.lang.Object
with ScalaObject{ def p(mi: MethodInner): Int } }

So, it probably isn't possible to satisfy the type of the parameter.
It somewhat makes sense that the method couldn't be called outside of
the method, but perhaps it should be callable within the method.

For that, a method with MethodInner in the signature would have to have
block-local visibility. It would then use the real MethodInner class
as the the parameter type and not a refinement as is currently done. I
don't have a use for any of this; I'm just trying to reason about it.

-Mark

> object Summary {
> class Outer {
> class Inner { }
> def f() = { class MethodInner ; new MethodInner }
> }
>
> // 1 static issue:
> //
> // Given method in MethodInner: def g(other: MethodInner) = ()
> // method1.g(method1) fails to compile with type error.
> //
> // Note that this cannot be worked around by widening the return type
> // of f() because MethodInner is declared inside of f. So there
> is no way
> // I see for a class declared inside a method to receive members of its
> // own declared type -- not only the narrow type of those from this
> // instance, but ANY members, because there is no Foo#Bar syntax
> which will
> // traverse a method.
> //
> // 4 runtime issues:
> //
> // From the outside: inner1.isInstanceOf[outer2.Inner] is
> true, should (maybe) be false
> // From inside inner1: inner2.isInstanceOf[Outer.this.Inner] is
> true, should (maybe) be false
> // From the outside: inner1 match { case _: outer2.Inner =>
> true ... } is true, should definitely be false
> // From inside method1: method2 match { case _: MethodInner =>
> true ... } is true, should definitely be false
> //
> // Note that the fact that every test returns true on instances
> of MethodInner means
> // that it is impossible to draw any type distinction between
> instances. As far as one
> // can tell, they are all of the same type regardless not only of
> whether they were
> // created on the same method invocation but whether they are
> contained in the same
> // instance of Outer.
> //
> // WRT "same method invocation", see Iterator.duplicate for an
> example of this.
> }
>
> --
> Paul Phillips | It's better to have gloved and tossed than never to
> Protagonist | have played baseball.
> Empiricist |
> up hill, pi pals! |----------* http://www.improving.org/paulp/ *----------
>
>

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: isInstanceOf vs. type matching: round 2

On Tue, Jan 19, 2010 at 05:58:51PM -0500, Mark Harrah wrote:
> Given section 6.11 of the specification, the behavior in runtime issue
> #4 seems reasonable to me. There is no proper supertype of
> MethodInner that is a nested class, so MethodInner is not treated as a
> nested class.

I admit with no small amount of shame that I wrote that whole email
without ever looking at the spec. Partly that's because the main issues
of interest to me are open issues with respect to how they're going to
be specified, but I think I should have read the spec on locally defined
class definitions...

So everyone can ignore the bits about the MethodInner class if they
don't resonate. The rest should be accurate.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland