java interfaces and Any parents, take 2

467 views
Skip to first unread message

Paul Phillips

unread,
Feb 11, 2013, 1:38:13 AM2/11/13
to scala-i...@googlegroups.com
I think leaving it that having any java interface as a parent kills your shot at being a value class will be a mistake. The essence of being a "value class" is statelessness. Java interfaces are stateless by definition. They ought to be perfect candidates. And they are.

If we can't require people to "opt in" to an AnyRef parent, we can still allow people to opt out.

  // the interface of java.lang.CharSequence, translated exactly
  // to scala so it can extend Any.
  trait CharSequence extends Any {
    def length: Int
    def charAt(index: Int): Char
    def subSequence(start: Int, end: Int): CharSequence
  }
  // I opt out of an AnyRef parent for CharSequence like this.
  final class Bippy(val xs: Array[Char]) extends AnyVal with CharSequence {
    def length = xs.length
    def charAt(index: Int): Char = xs(index)
    def subSequence(start: Int, end: Int): CharSequence = new Bippy(xs.slice(start, end))
  }

If a Bippy has to be passed to something looking for a CharSequence, then we box it, exactly like we already do in that situation.

  object Test {
    // public CharSequence f(char[]);
    def f(x: Bippy)        = x.subSequence(0, 1)
    // public CharSequence g(CharSequence);
    def g(x: CharSequence) = x.subSequence(0, 1)
  }

What concerns me beyond the obvious is the damage it is likely to do to java interop to have a cliff like this. If a single java interface poisons your value class, then in nontrivial situations you will certainly be forced to choose between performance and interop. It's a bitter pill under any circumstances, but it would be especially bitter here because, again, interfaces are stateless by definition.

Imagine a Bippy value class wrapping an Int which implements Comparable[Bippy] (or some identical interface with a different name, since Comparable is still special-cased.) Here is a method.

  def f(x1: Bippy, x2: Bippy) = x1 compareTo x2

This is what the implementation looks like if Comparable <: Any.

public int (int, int);
       0: getstatic     #19                 // Field jl/Bippy$.MODULE$:Ljl/Bippy$;
       3: iload_1
       4: iload_2
       5: invokevirtual #22                 // Method jl/Bippy$.compareTo$extension:(II)I
       8: ireturn

This is what it looks like if Comparable <: AnyRef.

public int f(rl.Bippy, rl.Bippy);
       0: aload_1
       1: aload_2
       2: invokevirtual #20                 // Method jl/Bippy.compareTo:(Ljl/Bippy;)I
       5: ireturn

It must be somewhere between 2 and 10 times slower - mighty rigorous handwaving I realize. I'll measure it if the exact size is at issue, but I suppose if 2x isn't enough, then 10x won't be either.

Adriaan Moors

unread,
Feb 11, 2013, 11:16:48 PM2/11/13
to scala-i...@googlegroups.com
Agreed. It seems only natural to think of a Java interface as declaring whatever flavor of Top necessary for the occasion. Can we treat purely abstract traits similarly?



--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Paul Phillips

unread,
Feb 11, 2013, 11:35:30 PM2/11/13
to scala-i...@googlegroups.com

On Mon, Feb 11, 2013 at 8:16 PM, Adriaan Moors <adriaa...@typesafe.com> wrote:
Can we treat purely abstract traits similarly?

I don't see why not. I was about to say there's less motivation in scala, but actually the same problem will be widespread: interfaces which easily could be participants in value classes, but which weren't declared as such (usually because value classes didn't exist when they were written.)

martin odersky

unread,
Feb 12, 2013, 2:14:36 PM2/12/13
to scala-internals
I am coming around to like the change. One thing less to think about: Should a purely abstract trait extend from Any, and thereby accept value classes, or extend from AnyRef and thereby allow eq/ne? 

We'd need to come up with a good definition of eq/ne on Any. 

It needs to be an equivalence relation and it needs to be stronger than ==. 

Also, this looks like it should be a SIP.

 - Martin

 - Martin


--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

Paul Phillips

unread,
Feb 13, 2013, 12:55:43 PM2/13/13
to scala-i...@googlegroups.com
On Tue, Feb 12, 2013 at 11:14 AM, martin odersky <martin....@epfl.ch> wrote:
We'd need to come up with a good definition of eq/ne on Any. 

Here is what I would expect eq to mean on Any: same representation. Same size, same bit layout. AnyRef eq is already a specialization of that definition - two AnyRefs are eq if they are pointers to the same location.

That definition has a useful aspect I did not anticipate: "stronger" falls out naturally, and recaptures some of the distinction we lost in 2.8 when we unified the behavior of boxed and unboxed primitives. Without having thought about it too long, the cases where we would strengthen the relationship going from == to eq are the same ones which are false in java but true in scala.

 Java:  TRUE 5 == 5l
       FALSE java.lang.Integer.valueOf(5) equals java.lang.Long.valueOf(5) 
       FALSE java.lang.Integer.valueOf(5) eq java.lang.Long.valueOf(5) // java calls eq '==' of course

Scala:  TRUE 5 == 5l
        TRUE java.lang.Integer.valueOf(5) == java.lang.Long.valueOf(5)
       FALSE java.lang.Integer.valueOf(5) eq java.lang.Long.valueOf(5)

  New: FALSE 5 eq 5l

Value classes compared with eq would receive the same transformation they receive for ==, so

  new V(X1) eq new V(X2)   iff   (X1 eq X2)

I do not suggest this would be perfectly consistent, which is not possible in any case. Without necessary pre-knowledge of java's interning of integers in a certain range, one might be rather surprised by this:

 TRUE 5 eq 5
 TRUE (5: Any) eq (5: Any)
 TRUE 2000 eq 2000
FALSE (2000: Any) eq (2000: Any)

There is no way to avoid it. On the plus side, we rescue the availability of the java NaN semantics.

 TRUE java.lang.Float.NaN equals java.lang.Float.NaN
FALSE java.lang.Float.NaN == java.lang.Float.NaN
 TRUE java.lang.Float.NaN eq java.lang.Float.NaN

There are blemishes, but I think it would be a great improvement.

James Iry

unread,
Feb 13, 2013, 1:16:30 PM2/13/13
to scala-i...@googlegroups.com
+1. That' s a very useful definition of eq. No primitive coercion, no type magic, no nothing. Just bits. That relegates eq to its proper use as a low level mechanism for low level stuff.

Erik Osheim

unread,
Feb 13, 2013, 1:18:19 PM2/13/13
to scala-i...@googlegroups.com
On Wed, Feb 13, 2013 at 09:55:43AM -0800, Paul Phillips wrote:
> There are blemishes, but I think it would be a great improvement.

Agreed. I think this idea is definitely worth pursuing.

-- Erik

Simon Ochsenreither

unread,
Feb 13, 2013, 1:23:07 PM2/13/13
to scala-i...@googlegroups.com
Great! Looks like this definition could also work for multi-field value types.

martin odersky

unread,
Feb 13, 2013, 2:28:01 PM2/13/13
to scala-internals
I like it. That looks like the right definition of eq/ne to me. 

 - Martin


On Wed, Feb 13, 2013 at 7:23 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
Great! Looks like this definition could also work for multi-field value types.

--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Grzegorz Kossakowski

unread,
Feb 13, 2013, 9:39:48 PM2/13/13
to scala-i...@googlegroups.com
On 13 February 2013 10:16, James Iry <jame...@typesafe.com> wrote:
+1. That' s a very useful definition of eq. No primitive coercion, no type magic, no nothing. Just bits. That relegates eq to its proper use as a low level mechanism for low level stuff.

+1!

--
Grzegorz Kossakowski
Scalac hacker at Typesafe
twitter: @gkossakowski

nuttycom

unread,
Dec 14, 2013, 2:21:16 PM12/14/13
to scala-i...@googlegroups.com
This semantic for eq is pretty exciting to me. May I also suggest that eq checks only compile when it doesn't require the arguments to be coerced? Such that 1l eq 1.0d wouldn't compile?

Kris

nuttycom

unread,
Dec 14, 2013, 2:23:10 PM12/14/13
to scala-i...@googlegroups.com
Oh... Of course I didn't read closely enough, and this is already part of the proposal. :)

Simon Ochsenreither

unread,
Feb 2, 2014, 9:51:13 PM2/2/14
to scala-i...@googlegroups.com

Agreed. It seems only natural to think of a Java interface as declaring whatever flavor of Top necessary for the occasion. Can we treat purely abstract traits similarly?

I had a stab at treating Java interfaces as universal traits when participating in defining a value class: https://github.com/soc/scala/compare/scala:master...soc:topic%3Binterfaces-any?expand=1

It's currently a hack, and fails right afterwards with ...

scala> class Foo(val i: Int) extends AnyVal with java.lang.Cloneable
<console>:7: error: class Foo inherits conflicting members:
  method getClass in class AnyVal of type ()Class[_ <: AnyVal]  and
  method getClass in class Object of type ()Class[_]
(Note: this can be resolved by declaring an override in class Foo.)
       class Foo(val i: Int) extends AnyVal with java.lang.Cloneable
             ^


... but maybe this is easy to fix. (Haven't looked deeply at it, just know that we do quite some magic with the result type of getClass.)

Opinions?
Reply all
Reply to author
Forward
0 new messages