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

Re: an open letter to scalac

5 replies
milessabin
Joined: 2008-08-11,
User offline. Last seen 33 weeks 3 days ago.

On Sat, Dec 17, 2011 at 11:27 PM, martin odersky wrote:
> On Sat, Dec 17, 2011 at 11:22 PM, Miles Sabin wrote:
>> On Sat, Dec 17, 2011 at 9:07 PM, Paul Phillips wrote:
>>> scalac, I have a burning question for you:
>>>
>>> class A {
>>>  // Why can't I get my hands on "MyType", if I want to write:
>>>  //   def otherMe: MyType
>>>  // You know you can do this, scalac! I will prove it to you:
>>>  def f = this
>>> }
>>
> We have been around this so often that I do not really want to go into
> it anymore. MyType is attractive at first, but then lets you down
> badly.

I know it's come up often, but I can't find any precise statement of
what the problems are. Maybe we need a FAQ entry to cover it?

And it really isn't clear to me that there's an insurmountable problem
here, given that the (boilerplatey) encoding is just fine,

trait A {
type MyType <: A
def foo(a : MyType) : MyType = a
}

trait B {
type MyType <: B
def foo(b : MyType) : MyType = b
}

/* final*/ class C {
type MyType = C
def foo(c : MyType) : MyType = c
}

val c1 = new C
val c2 = new C
val c3 : C = c1.foo(c2)

Can we not come up with some straightforward way to get rid of the
boilerplate? Why not define MyType as bounded by its enclosing type if
that type is non-final, and equal to it's enclosing type if that type
is final (and equal to this.type if the enclosing entity is an
object)?

Then we could write,

trait A { // Implicitly type MyType <: A
def foo(a : MyType) : MyType = a
}

trait B { // Implicitly type MyType <: B
def foo(b : MyType) : MyType = b
}

final class C { // Implicitly type MyType = C
def foo(c : MyType) : MyType = c
}

val c1 = new C
val c2 = new C
val c3 : C = c1.foo(c2)

What could possibly go wrong?

Cheers,

Miles

Rüdiger Klaehn
Joined: 2009-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: an open letter to scalac
On Sun, Dec 18, 2011 at 10:26 AM, Miles Sabin <miles [at] milessabin [dot] com> wrote:

And it really isn't clear to me that there's an insurmountable problem
here, given that the (boilerplatey) encoding is just fine,

 trait A {
   type MyType <: A
   def foo(a : MyType) : MyType = a
 }

 trait B {
   type MyType <: B
   def foo(b : MyType) : MyType = b
 }

 /* final*/ class C {
   type MyType = C
   def foo(c : MyType) : MyType = c
 }

As was explained to me in a recent thread, this is not enough. http://groups.google.com/group/scala-user/browse_thread/thread/b0d74ac17ae0ea08/89ebb5dc504c5b11?lnk=gst&q=question+about+type+members#89ebb5dc504c5b11 
To get roughly the same behavior with type members than with type arguments, you have to do this:
trait A {  type MyType <: A}
trait B extends A {  override type MyType <: B { type MyType <: B.this.MyType }}
trait C extends B {  override type MyType = C}
And even that breaks down as soon as you use super.xxx. In the end it IMHO is best to just use a type argument for this. 
Given that Martin is opposed to doing something about this at the language level, it would probably be best just to document how to deal with this scenario, and maybe provide a base trait. But something needs to be done about this, since it is really annoying...
andrevandelft
Joined: 2010-02-07,
User offline. Last seen 34 weeks 1 day ago.
Re: an open letter to scalac

Last November, I wrote a piece on this subject in the thread
"Actual complexity in Scala that warrants simplification";
I claim that MyType bears no real can of worms; only use it
in covariant positions.

Unfortunately there was no reply, as the thread dealt with
other issues as well. I hope Martin will comment now.

On 16 nov, 02:35, I wrote:

On 15 nov, 21:20, martin odersky wrote: >
MyType has a lot of hidden complexities. All formal treatments of
MyType > have included "precise types", which are an anathema to OOP.
Last year you wrote "To make it work and useful in
contravariant positions, you need to also introduce exact types. So
it's a much bigger can of worms than it looks at first".
http://www.scala-lang.org/node/6649

I think it is a kind of confusion that one would want to use
the "MyType" in contravariant positions; the can of worms is there.
IMO you only should want to use "MyType" in covariant positions.
It is useful enough there.
From the language specification: "p.type, where p is a path pointing
to a value expected to conform (§6.1) to scala.AnyRef. The type
denotes the set of values consisting of null and the value denoted by
p"
 If you would change the latter part of the definition by "the set
of values consisting of null and all instances of the class of p",
would there be any complexities? (apart from breaking the "trick"
of Tightened Pattern Match, but that use case might be covered in
another way).

The "covarianceness" would remain the same, IMO. would the new
definition be equal to what you mean by "Exact type"? Anyway, I would
want exactly this type, not the rather vague MyType that Kim Bruce
described.

About a decade ago, I built such an experimental extension to
Java: types "This" and "p.This" seemed well definable, and
implementation was quite easy (looking back, that is).

The extension also had inheritable constructors, also named
"This" "This" became the type of "this", and also the return type of
the clone() method and of the inheritable constructors.

These new constructs seemed useful to enrich Java collections with
a filter method, and with 2 constructors prescribed by the
Javadoc comments (one constructor parameterless, one constructor with
a Collection as a parameter).

E.g., interface Collection would get:

    This();     This(Collection col);     This filter(Predicate
p);

In AbstractCollection this would become:

    public This() {}     public This(Collection col)
{addAll(col);}     public This filter(Predicate p) {       This
result = new This();       ...// add all suitable elements to result 
     return result; 
    }

I think (but do not know yet) that there would be more use cases
like this. I wonder what the complexities for something similar in
Scala would be.

I don't see how something similar may be done using abstract types. (I
wrote part of this text on 20 June in the scala-language group; my use
case there was a bit flawed).

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: an open letter to scalac

> On Sat, Dec 17, 2011 at 11:27 PM, martin odersky wrote:>> We have been around this so often that I do not really want to go into>> it anymore. MyType is attractive at first, but then lets you down>> badly.
(I didn't see this message, did it not go to the list?)

If something has come up many times, but other fairly intelligent
people aren't convinced that there's a problem, then it is even more
important to explain the problem clearly than it would be under any
other conditions. Because:

- It comes up frequently because it's a gigantic PITA
- It is possible you are incorrect in your assessment

Given the fact that one can, and does, at a massive cost in
boilerplate, encode exactly what we're talking about, it is very
difficult to believe that insurmountable problems are posed by
eliminating the boilerplate.

I could massively simplify the collections with this. And after hours
of tweaking the stupid things when trying to sensibly combine Iterable
and Traversable, I'm not at all sure I am willing to continue with
that task as things stand presently. So again, this is not an idle
question. I ask with the highest level of interest, what is the
problem with exposing the non-single this-type in covariant positions?

Rex Kerr writes in http://www.scala-lang.org/node/6649 :
> Furthermore, I've so far had trouble creating recursive methods (among others) using this scheme, as the compiler notices that T and T#T are not guaranteed to be the same thing under any scheme I've yet been able to cook up.

That is what sunk my attempt. There is such an elegant representation
potentially available for collections, it just doesn't work. I think
I will polish up the elegant version and ask the question again as
"why doesn't this work: how can we make it work?"

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: an open letter to scalac

On Sun, Dec 18, 2011 at 2:34 AM, Rüdiger Klaehn wrote:
> And even that breaks down as soon as you use super.xxx. In the end it IMHO
> is best to just use a type argument for this.

Right, and then you end up with this.

T, Coll, CollSeq, This, ThisSeq. One type parameter for the element
type, four trying to keep track of what things are. What if things
could keep track of what they are all by themselves?

trait ParSeqViewLike[+T,
+Coll <: Parallel,
+CollSeq,
+This <: ParSeqView[T, Coll, CollSeq] with
ParSeqViewLike[T, Coll, CollSeq, This, ThisSeq],
+ThisSeq <: SeqView[T, CollSeq] with
SeqViewLike[T, CollSeq, ThisSeq]]
extends GenSeqView[T, Coll]
with GenSeqViewLike[T, Coll, This]
with ParIterableView[T, Coll, CollSeq]
with ParIterableViewLike[T, Coll, CollSeq, This, ThisSeq]
with ParSeq[T]
with ParSeqLike[T, This, ThisSeq]

Rüdiger Klaehn
Joined: 2009-06-02,
User offline. Last seen 42 years 45 weeks ago.
Re: an open letter to scalac
On Sun, Dec 18, 2011 at 4:12 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
On Sun, Dec 18, 2011 at 2:34 AM, Rüdiger Klaehn <rklaehn [at] googlemail [dot] com> wrote:
> And even that breaks down as soon as you use super.xxx. In the end it IMHO
> is best to just use a type argument for this.

Right, and then you end up with this.

T, Coll, CollSeq, This, ThisSeq.  One type parameter for the element
type, four trying to keep track of what things are.  What if things
could keep track of what they are all by themselves?

I agree completely that the type parameter approach has its problems. But at least it works with super.xxx.
At the very least there should be a detailed howto and best practice on how to deal with these problems. But every time I bring this up the answers imply that this is some kind of beginners problem, that I should use case classes and copy(), that I should not use inheritance in the first place, or that I should use a graph rewriting library. 
Looking at the intricate rube goldberg machinations used in the collections libraries, it seems that this is not a beginners problem at all. The tracking of MyType or however you want to call it is part of the reason that people want to hide the complete signatures of the collections libraries in order not to scare away beginners.  
trait ParSeqViewLike[+T,
                    +Coll <: Parallel,
                    +CollSeq,
                    +This <: ParSeqView[T, Coll, CollSeq] with
ParSeqViewLike[T, Coll, CollSeq, This, ThisSeq],
                    +ThisSeq <: SeqView[T, CollSeq] with
SeqViewLike[T, CollSeq, ThisSeq]]
extends GenSeqView[T, Coll]
  with GenSeqViewLike[T, Coll, This]
  with ParIterableView[T, Coll, CollSeq]
  with ParIterableViewLike[T, Coll, CollSeq, This, ThisSeq]
  with ParSeq[T]
  with ParSeqLike[T, This, ThisSeq]

Yuck!

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