Am 06.09.2018 um 02:23 schrieb MG:
Hi Jochen,
but in what sense is any of these examples confusing for the user ? Type
inference is not magic, and it can quickyl become a hard mathematical
problem (https://en.wikipedia.org/wiki/Type_inference).
And you as user do not have to understand the inferred type? I would
like to be able to do that without a mathematical degree.
But in all that
cases, we should just fall back to Object or throw. I don't know
Daniel's intentions, but for me type inference for methods (same as for
fields/variables) should only be used for simple, obvious cases, not for
complex ones (eveb if these are of course the only interesting
intellectual challnge ;-) ).
If it is only for simple cases it will always strike the user as a not
very powerful system and the user might be wondering what this inference
for the return type actually is... unless we call it "simple return type
inference" of course.
[...]
private foo() {
if (something) {
return x // of class X
} else {
return y // of class Y
}
}
if X and Y implement the interfaces Foo and Bar the common super type
would be something like Object+Foo+Bar, which cannot be an actual
return type, because the Java type system cannot properly express that
type. Which is it then? Object, or Foo or Bar?
Intuitively I would not infer on interfaces, but only classes. In
practice I would expect X and Y to have a common superclass that is not
Object; otherwise infer Object.
if we keep it simple we fail here, which means compilation error
And if you think this problem is small, you have to consider this one
here as well:
private foo() {
def ret
if (something) {
ret = x // of class X
} else {
ret = y // of class Y
}
return ret
}
Same problem as before obviously, just showing that using local
variables makes it even worse.
Infer Object - if you use Object for the return variable type, this is
what you should expect...
If I had use X or Y it would have failed compilation. That is my point here.
And how about this one?
private f(List<X> l) { if (l.size()==1) {
return l[0]
} else {
return f(l.tail())
}
}
for me it is obvious the return type is X, but a compiler must be able
to see through the recursive call and it must see that it is a
recursive call.
Too complex => infer Object
Too complex => fail compilation!
[...]
Or let us say there is also g(Object):Object and let us assume we
delete g(X):X. Then inferring the type above successfully means
obviously to let f return Object. The change will go unnoticed in f
and cause secondary errors in callers of f. In the worst case even
quite far away from the callsite itself.
I do not follow: What secondary errors would that be ? How would they
differ from errors that occur when the user explicitely supplies Object
as return type ?
lets say you have
def g() {
f()+1
}
int b = g()
let us say that the type for f changed from int to long, then g() will
still compile, but now it will also return long instead of int. This
will cause int b = g() to fail compilation because of loss of precision
from long to int. And to fix this you will have to fix not the
assignment, not g, but f.
bye Jochen