Bubba88
Bubba88

Reputation: 1920

Simple Type Inference in Scala

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

Answers (5)

Nikolay Ivanov
Nikolay Ivanov

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

GClaramunt
GClaramunt

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

Daniel C. Sobral
Daniel C. Sobral

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

oxbow_lakes
oxbow_lakes

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

Kevin Peterson
Kevin Peterson

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

Related Questions