简体   繁体   中英

How does Scala's type inference work with type bounds?

When type bounds exist on a type parameter, how exactly does Scala determine the type to infer? For example:

def onMouseClicked_=[T >: MouseEvent](lambda: T => Unit) = 
  setOnMouseClicked(new EventHandler[T] {
    override def handle(event: T): Unit = lambda(event)
  })

When attempting to use this function, such as in:

onMouseClicked = { me => doSomething() }

me will have the inferred type of MouseEvent . The type bound of T is a lower type bound, so T must either be of type MouseEvent or a supertype of MouseEvent , so why does me have the inferred type of MouseEvent ? Shouldn't it infer the most general type?

Is it something I'm not getting about how Scala's type inference works? Or is my understanding of type bounds completely wrong?

Edit:

Let's say we further restrict the the type of T to be a subtype of Event , where Event is a supertype of MouseEvent . So we get:

def onMouseClicked_=[T >: MouseEvent <: Event](lambda: T => Unit) = 
  setOnMouseClicked(new EventHandler[T] {
    override def handle(event: T): Unit = lambda(event)
  })

So if we do

onMouseClicked = { me: MouseDragEvent => doSomething() }

where MouseDragEvent is a subtype of MouseEvent , the compile fails with a type error, as expected, because the bound ensures that me must be a supertype of MouseEvent .

And yet if we do

onMouseClicked = { me: Any => doSomething() }

the compile succeeds. It's obvious that Any is not a subtype of Event , so why does the compile succeed? What is inferred type of T ?

I'll give this a try, but I'm not sure everything will be supercorrect or anyway clear.

Premise

The first point to address is that a function from type T to result R ( T => R ) is covariant in its return type and contravariant in its parameters: ie Function[-T, +R] .

To make it short it means that for function fsub to be a subtype of function f its return type must be the same type or a subtype of R (covariant) and its parameter must be the same or a supertype of T (contravariant).

Let's try to make sense of this: if you want to be able to use fsub where a f is expected (see Liskov), you need an fsub that can handle at most a passed argument of T , but nothing more specific, because that's what the callers of f are expected to pass to it. Moreover, since a T will be passed, you can be more relaxed on the parameters for your fsub and handle one of its supertypes, since every T passed to it is also an instance of its supertypes.

This is what is meant when we say that a function's argument is contravariant in its type: it tells you how its type can change in relationship subtyping the function as a whole, or actually the other way round.


Given this, let's get back to the specific case. The handler's mutator ( onMouseClicked_= ) should at least accept a lambda of type MouseEvent => Unit . Let's give it a name, handler: MouseEvent => Unit .

But it doesn't end here, you should expect to be able to pass the method a subtype of this lambda, and what will the type of this "subfunction" be? As we said before it could accept a MouseEvent or any supertype of it, then subhandler: T => Unit with T :> MouseEvent .

And that's exactly the general form of the method you saw.

Conclusion

I hope it became clear now that the reason why the method defines T as a (non-strict) supertype of MouseEvent is not because your handler will ever receive anything different from a MouseEvent , but because you can pass it a lambda that could be able to only handle a more abstract type of event... (eg you can use a me: Any => doSomething() )

I'm not entirely confident about the accuracy of this answer, but here goes...

The type of lamba is T => Unit , which is sugar for Function1[T, Unit] , which is defined as:

trait Function1[-T1, +R]

T is used for the first parameter T1 , which is contravariant, indicated by the - in -T1 . For a contravariant parameter, the lowest type in the hierarchy is the most general.

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