简体   繁体   中英

confusion with existential types in scala

I was struggling to understand existential types in scala but can't figure it out. Here is my test code:

scala> val a: Array[T] forSome {type T} = Array(1,2.2,"3")
a: Array[_] = Array(1, 2.2, 3)

scala> val b: Array[T forSome {type T}] = Array(1,2.2,"3")
b: Array[T forSome { type T }] = Array(1, 2.2, 3)

scala> a(0)
res35: Any = 1

scala> b(0)
res36: Any = 1

scala> a(0) = "x"
<console>:10: error: type mismatch;
 found   : String("x")
 required: T
              a(0) = "x"
                     ^

scala> b(0) = "x"

I was told that Array[T] forSome {type T} means any type of Array such as Array[Int], Array[String], etc. Array[T forSome {type T}] means Array[Any]. But according to the result of my test code, I can't see this difference, and the compile error message is also confusing. What does required: T means? I do appreciate if someone can give a detail explanation, thanks!

I was told that Array[T] forSome {type T} means any type of Array such as Array[Int], Array[String], etc.

That's correct. You might be surprised to see that the value Array(1,2.2,"3") is accepted as a value of type Array[T] forSome {type T} , even though Array(1,2.2,"3") is clearly not a value of type Array[Int] nor Array[String] . To understand what's going on, let's ask Scala what type it infers for Array(1,2.2,"3") :

scala> val x = Array(1,2.2,"3")
x: Array[Any] = Array(1, 2.2, 3)

Aha! So there is a type T , namely Any , so that x has type Array[T] . That's why Array(1,2.2,"3") is accepted as a value of type Array[T] forSome {type T} .

Array[T forSome {type T}] means Array[Any]

Also correct. The type <type expression involving T> forSome {type T} is the supertype of all types in which <type expression involving T> is instantiated at a particular T . So Array[T] forSome {type T} is the supertype of Array[Int] , Array[String] , etc. And since T forSome {type T} is the supertype of Int , String , etc., T forSome {type T} is the supertype of all types, aka Any .

 scala> a(0) res35: Any = 1 

You might be wondering why the result has type Any , not T . Compare with the following:

scala> (a(0), a(1))
res0: (T, T) forSome { type T } = (1,2.2)

As you can see, the type of the elements of Array[T] is indeed T . It's just that when we only fetch a single element from the array, it's type is T forSome { type T } , which simplifies to Any .

 scala> b(0) res36: Any = 1 

This time the type Any is very much expected, since the type of the elements of Array[T forSome {type T}] is T forSome {type T} , aka Any .

Note that since the forSome is inside the brackets, the type for a pair of elements...

scala>  (b(0), b(1))
res1: (Any, Any) = (1,2.2)

...is (T forSome {type T}, T forSome {type T}) , aka (Any, Any) .

 scala> a(0) = "x" <console>:10: error: type mismatch; found : String("x") required: T a(0) = "x" ^ 

The type of the value you assign to an Array[T] must be a subtype of T . We know that the value a has its T instantiated to Any , but when type checking, it's not the value's real type which matters, it is its declared type. And is String a subtype of T ? No, since a could be an Array[Int] and a String cannot be put into an Array[Int] .

 scala> b(0) = "x" scala> b res2: Array[T forSome { type T }] = Array(x, 2.2, 3) 

The type of the value you assign to an Array[T forSome {type T}] must be a subtype of T forSome {type T} aka Any . Is String a subtype of Any ? Yes, so the assignment succeeds.

One last thing about existentials: type inference works best when T only has to be instantiated once. Earlier we saw that (a(0), a(1)) produced a pair of elements which both have the same (unknown) type. I was actually surprised that this worked. In the following very similar example, a(0) and a(1) are also supposed to have the same type, and so the assignment is supposed to succeed, but it doesn't.

scala> a(0) = a(1)
<console>:12: error: type mismatch;
 found   : (some other)T(in value a)
 required: T(in value a)
       a(0) = a(1)
               ^

To show Scala that such an assignment is type safe, we can wrap it in a function which works for any Array[T] :

scala> def modifyArray[T](arr: Array[T]) =
     |   arr(0) = arr(1)
modifyArray: [T](arr: Array[T])Unit

We can now perform the assignment by applying our function to a , and this time it does succeed.

scala> modifyArray(a)

scala> a
res3: Array[_] = Array(2.2, 2.2, 3)

It's not the same as Array[Any] . Array[T forSome { type T; }] Array[T forSome { type T; }] means that you don't care what type inside of Array . You can write generic methods over Array eg: def swap(arr : Array[T forSome { type T; }]) = arr(0) = arr(1); . But Array type stays known and all expressions is typechecked with respect to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM