Minor revelation about Scala existential types

So, I’ve just realised why Scala’s existential types are several orders of magnitude more powerful than Java’s wildcards.

   def swap(xs : Array[T forSome { type T; }]) = xs(0) = x(1); 

The type is not completely unknown, and is persisted across method invocations, so for a given fixed instance you can make use of that to perform operations that would be impossible with wildcards. In particular the following can’t work:

  public void swap(List<?> xs){ 
    xs.set(0, xs.get(1));
  }

This can’t work, because Java has no way of determining that the two wildcards in xs.set and xs.get are the same.


Comments

ijuma on 2008-01-09 20:50:00:

Try the following in your favourite Java compiler (using [] for generics):

public [T] void swap(List[? extends T] xs){
swap0(xs);
}

private [T] void swap0(List[T] xs) {
xs.set(0, xs.get(1));
}

Ugly, but works.

Ismael

David R. MacIver on 2008-01-09 21:00:00:

Yeah. You can basically encode existentially quantified types by moving the type parameter around and universally quantifying instead. This gets really annoying outside of non-trivial examples though - to the point where I usually just give up and cast.

David on 2008-01-09 21:49:00:

Not understanding something here:

public [T] void swap(List[T] xs) {
xs.set(0,xs.get(1));
}

completely valid in java (1.6) and generates no errors or warnings.

so used in the context of:
List[String] xs = new ArrayList[String]();
swap(xs);

it will work correctly (other than the out of bounds exception since I didn’t populate the list)

what am I missing?

David R. MacIver on 2008-01-09 21:54:00:

True. That was probably a bad example because of the misleading method.

A better one is as follows:

def foo(xss : Array[Array[T : forSome { type T; }]] = for(xs <- xss) {
xs(0) = xs(1);
}

The point is that this works locally without you having to extract out all your logic into a single method and pass it that way. Sometimes the extracted method is actually useful and you should do that, but this removes a lot of pain points when you have relatively simple code.

Marius on 2008-01-18 22:56:00:

So where is the revelation?

def foo[T](xss : Array[Array[T]]) = for(xs <- xss) {
xs(0) = xs(1);
}

seems to be equivalent to your example. So why using an existential type in your example?

In Java one can capture the wildcard with a T.

P.S.
I also like Scala very much and I’d like to have a better understanding of existential types although so far it seems to me that their usefulness is related with packing types without using inference of the polymorphic method

David R. MacIver on 2008-01-18 23:57:00:

Those two don’t do the same thing though.

Array(Array(“foo”), Array(1)) is a perfectly valid Array[Array[T forSome { type T; }]], but it isn’t an Array[Array[T]] for any given T. The point is that T can assume different values for different members of the outer array.

Marius on 2008-01-19 17:00:00:

having :

def foo(xss : Array[Array[T forSome { type T; }]]) = for(xs <- xss) {
xs(0) = xs(1);
}


attempting to call it like:
foo1(Array(Array(“foo”), Array(3)));

yields the following compiler error :

“inferred type arguments [T forSome { type T }] do not conform to method apply’s type parameter bounds [A <: AnyRef]”

similarly:

val t: Array[Array[T forSome { type T; }]] = Array(Array(“foo”), Array(1))

is not a valid declaration.

(I’m using Scala 2.6.1)

Any thoughts?

Marius on 2008-01-19 17:12:00:

One more thing:

having

val t: Array[Array[T forSome {type T}]] = Array(Array(“foo”), Array(“1”))

yields the same compiler error whereas:

val t: Array[Array[T forSome {type T <: String}]] = Array(Array(“foo”), Array(“1”))

is a correct declaration.

... so far I could not use existential types to abstract different types. Am I doing something wrong?

P.S. - there is atypoe above I declared foo but invoked foo1. Just ignore this

Marius on 2008-01-19 17:23:00:

doing a different approach:

having:

class C[T](x: T*)

and
def foo(xs : C[C[T forSome {type T;}]]) = { }

foo(new C(new C(“foo”), new C(1)));

compiles correctly

whereas

having:

def foo[T](xs : C[C[T]]) = { }

does not compile.

so you’re right the two declarations are not the same (just beautiful) but still I’m not clear why the Array’s example does not compile. Arrays apply method is parameterized like [A <: Any] but obviously a type T is ultimately an Any.

Best of drmaciver.com | David R. MacIver on 2014-01-12 13:13:29:

[…] A little detail on the difference between Java wildcards and Scala existential types. […]