Help for a newbie ..

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Help for a newbie ..

Debasish Ghosh-2
Hi -

I am trying to learn Scala and have hit upon the wall while trying to understand
abstract data types .. I have the following snippet :

object ShapeTest extends Application {

    class Point(x : int, y : int) {
        override def toString() = "[" + x + "," + y + "]"
    }

    abstract class Shape {
        def draw() : unit
    }
   
    class Line(s : Point, e : Point) extends Shape {
        def draw() : unit = { Console.println("draw line " + s + "," + e) }
    }
   
    abstract class Foo {
        type T <: Object
       
        def show(o : T) : unit
        def print() : unit = {Console.println("in Foo")}
    }
   
    abstract class ShapeFoo extends Foo {
        type T <: Shape
        def show(o : T) : unit = { o.draw() }
        override def print() : unit = {Console.println("in ShapeFoo")}
    }
   
    class LineFoo extends ShapeFoo {
        type T = Line
        override def print() : unit = {Console.println("in LineFoo")}
    }
   
    val p1 = new Point(1,4)
    val p2 = new Point(12, 28)
   
    val l1 = new Line(p1, p2)

   
    val l = new ShapeFoo{  // ** //
        type T = Line  // ** //
        override def print() : unit = {Console.println("in LineFoo")} // ** //
    } // ** //
    l.show(l1) // ** //
}

When I compile the above program, I get the following error :

D:\scala-2.1.0\dg>scalac shape.scala
shape.scala:43 error: type mismatch;
 found   : ShapeTest.this.Line
 required: ShapeTest.this.l.T
    l.show(l1)
           ^
one error found

But when I replace the last 5 lines (marked with // ** //) with :

    val l = new LineFoo()
    l.show(l1)

the program compiles fine. All I want to do is use the anonymous class with
inline redefinition of the abstract type.

Any help will be appreciated. BTW I am using Scala 2.1.0.

Thanks.

- DG

Reply | Threaded
Open this post in threaded view
|

Re: Help for a newbie ..

Martin Odersky
Dear Debashish:

Thanks for your mail. The problem in your code is in the last lines:

   val l = new ShapeFoo{
     type T = Line
     override def print() : unit = {Console.println("in LineFoo")}
   }
   l.show(l1)

Here, the type of `l' is just ShapeFoo, which means that the type
binding type T = Line is forgotten. This is a consequence of the typing
rules for anonymous instance creation expressions and blocks (Section
6.7 and 6.8 of the Spec).

You can recover the missing type information in two ways.

Either you turn `l' into an Object:

   object l extends ShapeFoo{
     type T = Line
     override def print() : unit = {Console.println("in LineFoo")}
   }
   l.show(l1)

Or you add the type alias implicitly as a refinement:

   val l: ShapeFoo { type T = Line } = new ShapeFoo{
     type T = Line
     override def print() : unit = {Console.println("in LineFoo")}
   }
   l.show(l1)

That said, I think the Scala spec might be generalized a bit in the
future order to allow code like the one you wrote. I'll keep it in my mind.

Cheers

  -- Martin
Reply | Threaded
Open this post in threaded view
|

Re: Help for a newbie ..

Debasish Ghosh-2
Martin Odersky <martin.odersky <at> epfl.ch> writes:

>
> Dear Debashish:
>
> Thanks for your mail. The problem in your code is in the last lines:
> <snippet removed>
> ...


> ...
> That said, I think the Scala spec might be generalized a bit in the
> future order to allow code like the one you wrote. I'll keep it in my mind.
>
> Cheers
>
>   -- Martin
>
>


Dear Martin -

Thanks for the clarification. But still one question confuses me.
A similar code (please see the fragment below) compiles OK in Scala 1.4.0.4,
but of course gives the same error (which I mentioned in my previous post) in
Scala 2.1.2.
Has there been any change in typing rules for anonymous instance creations in
Scala 2 ?

object GenericsTest with Application {

   abstract class Sum {
     type t;
     def add(x: t, y: t): t = x;
   }

   object intSum extends Sum {
     type t = Int;
     override def add(x: Int, y: Int): Int = x + y;
   }
   
   class ListSum[a] extends Sum {
     type t = List[a];
     override def add(x : List[a], y : List[a]) : List[a] = x ::: y;
   }
   
   val i = new Sum{type t = Int}.add(12, 16);
   Console.println(i);
}

Best Regards.
- DG

Reply | Threaded
Open this post in threaded view
|

Re: Help for a newbie ..

Martin Odersky
Debasish Ghosh wrote:

> Dear Martin -
>
> Thanks for the clarification. But still one question confuses me.
> A similar code (please see the fragment below) compiles OK in Scala 1.4.0.4,
> but of course gives the same error (which I mentioned in my previous post) in
> Scala 2.1.2.
> Has there been any change in typing rules for anonymous instance creations in
> Scala 2 ?
>
>  
That's correct. The old compiler implemented a somewhat ad-hoc rule that
type member definitions would be added as a refinement of the type of an
anonymous instance creation but value members would not. The new version
first erred on the conservative side -- by eliminating all members in a
refinement unless they were given explicitly. But that's not very
intuitive, it seems. I have now verified that nothing in our codebase
would break if we generalize the rule, admitting all members in the
refinement.

Looking at it from the language definition side, here is the issue:
Let's suppose we have a class

  class Foo { type T; val x: Any }

An anonymous instance creation

  new Foo { type T = int; val x = 1 }

is seen as equivalent to the block

  { class $anon extends Foo { type T = int; val x = 1 }; new $anon }

What should the type of that block be? Normally, it would be $anon,
except that this is illegal because the name $anon is not visible
outside the block. The current spec says it is instead the parent type
of $anon, which is Foo. Unfortunately, this forgets about the overriding
definitions in the anonymous class. The proposed revised spec would say
that the type of the block is the least proper supertype of the
anonymous class. This happens to be the type

  Foo { type T = int; val x: int }

which is what we want, I think. The change will probably be rolled into
the next Scala release (2.1.4).

Thanks for bringing this up!

 -- Martin