Greg
Greg

Reputation: 11532

Why does wrapping values in a code block change my typed numbers into Doubles?

If I call this with 3 I get back an Int-typed return as I expect.

  def determineKind( a:Int ):Any = {
    if( a < 5 ) 
      a.toInt
    else if(a<50)
      a.toLong
    else if(a<100)
      a.toFloat
    else
      a.toDouble
  }

If I call this with 3, I get back a Double.

  def determineKind2( a:Int ):Any = {
    val aResult = { if( a < 5 ) 
      a.toInt  // Execution flows here
    else if(a<50)
      a.toLong
    else if(a<100)
      a.toFloat
    else
      println("Oops!") // Never executed--yet I get a Double back!
      a.toDouble  
    }
    aResult
  }

Why? I don't want the Double. (The real function uses a match/case block, which also seems to convert any of these numerical types to Double.)

Something about wrapping things in a code block triggers the unwanted type change to Double.

Why is that?

Upvotes: 3

Views: 85

Answers (3)

Pawel Gebal
Pawel Gebal

Reputation: 1

Greg, this block below returns value of type Double because the last thing you do in it is calling toDouble method on a.

val aResult = {
    if( a < 5 ) 
      a.toInt  // Execution flows here
    else if(a<50)
      a.toLong
    else if(a<100)
      a.toFloat
    else
      println("Oops!") // Never executed--yet I get a Double back!
      a.toDouble // <---- this line is not a part of if else expression
}

That's a good reason why to use curly braces, so you can prevent mistakes like this.

Upvotes: 0

pedrofurla
pedrofurla

Reputation: 12783

You problem lies in how you defined the if block, effectively, in determineKind2, a.toDouble is always assigned to aResult and the entire result of the if block is discarded.

The illustration of what is happening:

def block = {
  if(condition)
     v1
  else 
     println(v2)
     v2 
}

Always returns v2. This happens because the same code using explicit curly braces would be this:

def block = {
  if(condition) {
     v1
  } else {
     println(v2)
  }
     v2 
}

Notice how v2 is outside the if block. In case statement block on the other hand:

condition match {
  case true => v1
  case _    =>
    println(v2)
    v2
}

The same doesn't happen above, the blocks in cases are ended by the start of another case in the same scope depth.

As a side note: indentation doesn't have any effect in how blocks are compiled.

Upvotes: 4

0__
0__

Reputation: 67310

This is a tricky situation where numeric widening comes into play. In the first example, you are stating the return type, Any, and thus there is no need to widen any of the types. In the second case, val aResult = you are asking Scala to infer the type of the if-expression, and it will avoid to infer Any and instead decide to widening the individual types numerically to a Double. Often this widening is what you'd want, e.g. say you write Seq(1, 2.3, 4.5) - you'd expect that to be a Seq[Double] and not Seq[Any].

If you define the type of aResult, you ask Scala to accept the unspecific type of Any:

def determineKind(a: Int): Any = {
  val res: Any /* ! */ = if (a < 5)
    a.toInt
  else if (a < 50)
    a.toLong
  else if(a < 100)
    a.toFloat
  else
    a.toDouble

  res
}

assert(determineKind(3).isInstanceOf[Int])

Numeric widening appears in section 6.26.1 of the Scala Language Specification:

If e has a primitive number type which weakly conforms to the expected type, it is widened to the expected type using one of the numeric conversion methods toShort, toChar, toInt, toLong, toFloat, toDouble defined here.

Upvotes: 2

Related Questions