Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Existensial types in Scala

Little confusing about existential types.

This works for me:

def valueOf(c: Class[_], name: String) {
  type C = Class[T] forSome {type T <: Enum[T]}
  Enum.valueOf(c.asInstanceOf[C], name)
} 

but this does not:

def valueOf(c: Class[_], name: String) {
  type T = T forSome {type T <: Enum[T]}
  Enum.valueOf(c.asInstanceOf[Class[T]], name)
}

In my mind both expressions are equivalent to:

Enum.valueOf(z.asInstanceOf[Class[T] forSome {type T <: Enum[T]}], name)

But Scala says that it's in my mind only:

inferred type arguments [T] do not conform to method valueOf's type parameter bounds [T <: Enum[T]]
         Enum.valueOf(c.asInstanceOf[Class[T]], name)
              ^
like image 300
dk14 Avatar asked Aug 23 '13 06:08

dk14


People also ask

What is an existential type?

Existential types, or 'existentials' for short, are a way of 'squashing' a group of types into one, single type. Existentials are part of GHC's type system extensions.

What is .type in Scala?

Type declaration is a Scala feature that enables us to declare our own types. In this short tutorial, we'll learn how to do type declaration in Scala using the type keyword. First, we'll learn to use it as a type alias. Then, we'll learn to declare an abstract type member and implement it.

What is an existential type in Swift?

Existentials in Swift allow defining a dynamic value conforming to a specific protocol. Using primary associated types, we can constrain existentials to certain boundaries. The Swift team introduced the any keyword to let developers explicitly opt-in to a performance impact that might otherwise not be visible.


1 Answers

Consider the difference between the following two expressions:

Enum.valueOf(x.asInstanceOf[Class[X] forSome { type X <: Enum[X] }], name)

And:

Enum.valueOf(x.asInstanceOf[Class[X forSome { type X <: Enum[X] }]], name)

Now think about how the type parameter T of valueOf will be inferred in each of these cases. In the first case, we've got an X that we know is a subtype of Enum[X], and we're all set. In the second case, on the other hand, T would have to be X forSome { type X <: Enum[X] }, and crucially this type is not a subtype of Enum[X forSome { type X <: Enum[X] }], so we haven't satisfied the constraint on T.

The problem is that your second example is equivalent to the latter.


As a footnote, this would work just fine if Enum were covariant in its type parameter. Take the following simplified example:

trait Foo[A]
trait Bar[A]

def foo[A <: Bar[A]](f: Foo[A]) = f

def x: Foo[X] forSome { type X <: Bar[X] } = ???
def y: Foo[Y forSome { type Y <: Bar[Y] }] = ???

Now foo(x) will compile, but foo(y) won't, just as in your code. But change Bar a bit:

trait Foo[A]
trait Bar[+A]

def foo[A <: Bar[A]](f: Foo[A]) = f

def x: Foo[X] forSome { type X <: Bar[X] } = ???
def y: Foo[Y forSome { type Y <: Bar[Y] }] = ???

Now they'll both compile. I'd guess that this has something to do with the reason that we have such strong intuitions about your two examples being equivalent.


As another footnote (in response to gzmo's comment below), consider the following:

scala> trait Foo[A <: Foo[A]]
defined trait Foo

scala> class MyFoo extends Foo[MyFoo]
defined class MyFoo

scala> val myFoo = new MyFoo
myFoo: MyFoo = MyFoo@3ee536d

scala> myFoo: (X forSome { type X <: Foo[X] })
res0: X forSome { type X <: Foo[X] } = MyFoo@3ee536d

scala> myFoo: Foo[MyFoo]
res1: Foo[MyFoo] = MyFoo@3ee536d

Let's suppose that X forSome { type X <: Foo[X] } were a subtype of Foo[X forSome { type X <: Foo[X] }] (ignoring for a moment the fact that the latter isn't even a valid type). Then we'd be able to write the following:

myFoo: Foo[X forSome { type X <: Foo[X] }]

But Foo is invariant, so if we have some thing that's an instance of both Foo[A] and Foo[B], then it must be the case that A =:= B. But it's definitely not the case that MyFoo =:= (X forSome { type X <: Foo[X] }). Not sure all of that is any less confusing, but it's how I convinced myself that the compiler knows what it's doing here.

like image 199
Travis Brown Avatar answered Sep 28 '22 09:09

Travis Brown