简体   繁体   中英

Why can't Scala infer the type parameter in this example?

Suppose I have two classes, Input and Output , which are designed to be connected to each other. Output produces values of some type, and Input consumes them.

class Input[T] {
  var output: Option[Output[_ <: T]] = None
}
class Output[T] {
  var input: Option[Input[_ >: T]] = None
}

It's okay if an Input and Output pair don't operate on the same kind of value as long as the Input type parameter is a supertype of the Output type parameter. Note that the type parameter in both classes is invariant; in the real versions it is used in both co- and contravariant positions.

I have a connect method elsewhere which sets up a link between an Input / Output pair:

def connect[T](output: Output[T], input: Input[_ >: T]) = {
  output.input = Some(input)
  input.output = Some(output)
}

If I call this method as below, I get a type error:

val out = new Output[String]
val in = new Input[AnyRef]
connect(out, in)

The error is:

test.scala:17: error: type mismatch;
 found   : Output[String]
 required: Output[AnyRef]
  connect(out, in)
          ^

I can resolve this by writing out the type parameter (in this case, I would write connect[String] , but I think the compiler should be able to figure this out for me. How can I change the connect method so that the type parameter is inferred automatically?


Edit: For now, I've made connect a method of Output so it gets the type parameter automatically. This also has the added benefit that I can use the infix notation out connect in , but the design feels a little awkward.

I am still interested in why the compiler exhibits this behavior. I feel like it should be able to infer the type parameter. Is this actually working as specified?

You will sometimes get better results if you use multiple parameter lists:

def connect[T](output: Output[T])(input: Input[_ >: T]) = {
  output.input = Some(input)
  input.output = Some(output)
}

connect(out)(in)

...and indeed in this case, it works.

I may be totally wrong, but I think the problem is when you tie together the input and the output: the input has an output restricted to a subtype of T, but the output has an input restricted to a supertype of T, the only type that can satisfy both conditions is T.

Input[T] -> Output[ _ <: T ]
Output[Q] -> Input[ _ >: Q ]

When you create the input with the output (replacing Q with _ <: T )you get:

Input[T]->Input[ _ >: [_ <: T] ]

The same as

Input[T]->Input[ _ <: T <: _ ]

Hence the type mismatch

Actually scala type inferene can't handle "recursion" for now. So if type of argument can be infered only in collocation with other argument scala fail to inference. But if you use differnt argument list scala f(a)(b)(c,d) will infer types list by list, so it usally works better.

PS It is oversimplified, but can get you some clue.

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