简体   繁体   中英

Does it make sense to return an existential type?

If I define a method which returns an existential type:

trait X[T] {
  def hello(x: T):Unit = println(x)
}

def myX: X[_] = new X[Any] {}

If I invoke myX , I will get a value which has type X[_] , but I can't passing anything to it:

val y: Any = 123
myX.hello(y)  // compilation error!

Only I can do is passing a Nothing :

val y: Nothing = ???
myX.hello(y)  // compilable, but useless

So it doesn't make sense for a method to return an existential type?

scala> trait X[T] {
     |   def hello(x: T):Unit = println(x)
     | }
defined trait X

Your definition of a function:

scala> def myX: X[_] = new X[Any] {}
myX: X[_]

scala> val xnothing = myX
xnothing: X[_] = $anon$1@5c44c582

scala> xnothing.hello(1)
<console>:14: error: type mismatch;
 found   : Int(1)
 required: _$1
       xnothing.hello(1)
                      ^

"Fixed" definition of the same function - captures type arg properly:

scala> def myY[T]: X[T] = new X[T] {}
myY: [T]=> X[T]

If generic arg is not passed, infers Nothing .

scala> val ynothing = myY
ynothing: X[Nothing] = $anon$1@53f0a4cb

You can pass a generic arg explicitly.

scala> val yint = myY[Int]
yint: X[Int] = $anon$1@555cf22

Obviously Int is not Nothing .

scala> ynothing.hello(1)
<console>:14: error: type mismatch;
 found   : Int(1)
 required: Nothing
       ynothing.hello(1)
                      ^

This obviously works because we passed Int explicitly:

scala> yint.hello(1)
1

Now this is an interesting part. It works because Scala can figure out that generic arg is Int (from usage). contrast it with ynothing definition above and its call ynothing.hello(1) which does not compile.

scala> myY.hello(1)
1

It doesn't make much sense to return this existential type, but they can be useful in other cases. As a trivial example

// method which returns an existential type
// note that you can't return Array[Any] here,
// because arrays aren't covariant in Scala (which is a good thing)
def array: Array[_] = if (some condition) Array[Int](...) else Array[Double](...) 

// all of following works
array(0)
array.length
// etc.

Of course, if you can write the precise generic type of a method, you should definitely prefer 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