Reputation: 1920
I have been looking at type inference in Scala and there are a couple of things I'd like to understand a bit better around why expression/method-return types have to be explicitly declared in a few cases.
Explicit return
declaration
Example (works if return
keyword is ommitted):
def upCase(s: String) = {
if (s.length == 0)
return s // COMPILE ERROR - forces return type of upCase to be declared.
else
s.toUpperCase()
}
Why can't I use the explicitly typed parameter as a return value without declaring the return type? And that's not only for direct parameter references, just for any 'type-inferable' expression.
Method overloading
Example (fails to compile when the second joiner
method is added):
def joiner(ss: List[String], sep: String) = ss.mkString(sep)
def joiner(ss: List[String]) = joiner(strings, " ") // COMPILE ERROR WHEN ADDED
Upvotes: 3
Views: 1409
Reputation: 8935
Well most obvious answer is: because it stated in specification see part 6.20 of scala reference. But why it was designed this way is indeed very intresting question. I suspect it connected to the fact that compiler can't predict that expression will be the last one, since return changes execution flow.
EDIT:
Consider if return doesn't require explicit return type following code:
def bar() = {
if(guard())
return "SS"
else if(gurard1())
return true
2
}
that return type should bar have in this situation? Well there is option with most common supertype, but I think it will get us to returning Any in many cases. Well this is just my thoughts which may be totally incorrect =)
Upvotes: 3
Reputation: 3158
I suspect the method overloading (lack of) inference is related to the similar problem with recursive calls, because if the overloaded methods doesn't call each other, it works perfectly:
def joiner1(ss: List[String], sep: String) = ss.mkString(sep)
def joiner(ss: List[String], sep: String) = ss.mkString(sep)
def joiner(ss: List[String]) = joiner1(ss, " ")
There's two overloaded joiner methods, but the types are inferred correctly the code compiles.
Upvotes: 0
Reputation: 297155
The type of a function or method is inferred from the type of its last statement. Usually, that's an expression.
Now, "return
" breaks the control flow. It is an "immediate interrupt", so to speak. Because of that, the normal rules used to infer the type of an expression can't be used anymore. It still could be done, of course, but I'm guessing the cost in compiler complexity was deemed to high for the return.
Here's an example of how the flow is broken:
def toNumber(s: String) = {
if (s == null)
return ""
if (s matches """\d+""")
s.toInt
else
0
}
Normally, the type of the second if
statement would be used to infer the type of the whole function. But the return
on the first if
introduces a second return point from the function, so this rule won't work.
Upvotes: 2
Reputation: 134260
Disclaimer - this answer was directed to the question as it was originally posted
Scala's type inference already does infer the return type of a method / expression:
scala> def foo(s : String) = s + " Hello"
foo: (String)java.lang.String
scala> var t = foo("World")
t: java.lang.String = World Hello
and:
scala> def bar( s : String) = s.toInt
bar: (String)Int
scala> var i = bar("3")
i: Int = 3
and:
scala> var j = if (System.getProperty("user.name") == "oxbow") 4 else "5".toInt
j: Int = 5
EDIT - I didn't realize that the inclusion of the return
keyword meant that the return type of an expression had to be explicitly declared: I've pretty much stopped using return
myself - but it's an interesting question. For the joiner
example, the return type must be declared because of overloading. Again, I don't know the details as to why and would be interested to find out. I suspect a better-phrased question subject would elicit an answer from the likes of James Iry, Dan Spiewak or Daniel Sobral.
Upvotes: 0
Reputation: 7297
Type inference infers the return type of a method when it can, which is more or less in any case that the method isn't recursive.
Your example would work if you changed it to:
def upCase(s: String) = {
if (s.length == 0)
s // note: no return
else
s.toUpperCase()
}
I don't know offhand why the return changes this.
Upvotes: 1