Sudipta Deb
Sudipta Deb

Reputation: 1050

Use of break in Scala With Return Value

I have the below requirement where I am checking whether a value is greater than 10 or not and based on that I will break, otherwise I will return a String. Below is my code:

import scala.util.control.Breaks._

class BreakInScala {
  val breakException = new RuntimeException("Break happened")

  def break = throw breakException

  def callMyFunc(x: Int): String = breakable(myFunc(x))

  def myFunc(x: Int): String = {
    if (x > 10) {
      "I am fine"
    } else {
      break
    }
  }
}

Now what is the happening is that I am getting the error message saying "type mismatch; found : Unit required: String" The reason is :

def breakable(op: => Unit) 

But then how I will write a function which can return value as well as break if required?

Upvotes: 0

Views: 871

Answers (2)

danielkza
danielkza

Reputation: 2889

The Scala compiler can evaluate that a branch throws an exception and not use it to form a minimum bound for the return type, but not if you move the throwing code out in a method: since it can be overridden, the compiler cannot be sure it will actually never return.

Your usage of the Break constructs seems confused: it already provides a break method, there is no need to provide your own, unless you want to throw your exception instead, which would make using Break unnecessary.

You are left with a couple of options then, since I believe usage of Break is unnecessary in your case.

1) Simply throw an exception on failure

def myFunc(x: Int): String = {
  if (x > 10) {
    "I am fine"
  } else {
    throw new RuntimeException("Break happened")
  }
}

def usemyFunc(): Unit = {
  try {
    println("myFunc(11) is " + myFunc(11))
    println("myFunc(5) is " + myFunc(5))
  } catch {
    case e: Throwable => println("myFunc failed with " + e)
  }
}

2) Use the Try class (available from Scala 2.10) to return either a value or an exception. This differs from the previous suggestion because it forces the caller to inspect the result and check whether a value is available or not, but makes using the result a bit more cumbersome

import scala.util.Try

def myFunc(x: Int): Try[String] = {
  Try {
    if (x > 10) {
      "I am fine"
    } else {
      throw new RuntimeException("Break happened")
    }
  }
}

def useMyFunc(): Unit = {
  myFunc match {
    case Try.Success(s) => println("myFunc succeded with " + s)
    case Try.Failure(e) => println("myFunc failed with " + e)
  }
}

3) If the thrown exception isn't relevant, you can use the Option class instead. You can see how the multiple ways of working with Options relate to each other in this great cheat sheet.

def myFunc(x: Int): Option[String] = {
  if (x > 10) {
    Some("I am fine") /* Some(value) creates an Option containing value */
  } else {
    None /* None represents an Option that has no value */ 
  }
}

/* There are multiple ways to work with Option instances.
   One of them is using pattern matching. */

def useMyFunc(): Unit = {
  myFunc(10) match {
    case Some(s) => println("myFunc succeded with " + s)
    case None => println("myFunc failed")
  }
}

/* Another one is using the map, flatMap, getOrElse, etc methods.
   They usually take a function as a parameter, which is only executed
   if some condition is met.

   map only runs the received function if the Option contains a value,
   and passes said value as a parameter to it. It then takes the result
   of the function application, and creates a new Option containing it.

   getOrElse checks if the Option contains a value. If it does, it is returned
   directly. If it does not, then the result of the function passed to it
   is returned instead.

   Chaining map and getOrElse is a common idiom meaning roughly 'transform the value
   contained in this Option using this code, but if there is no value, return whatever
   this other piece of code returns instead'.
*/

def useMyFunc2(): Unit = {
  val toPrint = myFunc(10).map{ s =>
    "myFunc(10) succeded with " + s
  }.getOrElse{
    "myFunc(10) failed"
  }

  /* toPrint now contains a message to be printed, depending on whether myFunc
     returned a value or not. The Scala compiler is smart enough to infer that
     both code paths return String, and make toPrint a String as well. */

  println(toPrint)
}

Upvotes: 2

Jonny Coombes
Jonny Coombes

Reputation: 575

This is a slightly odd way of doing things (throwing an exception), an alternative way of doing this might be to define a "partial function" (a function which is only defined only a specific subset of it's domain a bit like this:

scala> val partial = new PartialFunction[Int, String] {
       | def apply(i : Int) = "some string"
       | def isDefinedAt(i : Int) = i < 10
       }

partial: PartialFunction[Int, String] = <function1>

Once you've defined the function, you can then "lift" it into an Option of type Int, by doing the following:

scala> val partialLifted = partial.lift
partialOpt: Int => Option[String] = <function1>

Then, if you call the function with a value outside your range, you'll get a "None" as a return value, otherwise you'll get your string return value. This makes it much easier to apply flatMaps/ getOrElse logic to the function without having to throw exceptions all over the place...

scala> partialLifted(45)
res7: Option[String] = None

scala> partialLifted(10)
res8:  Option[String] = Some(return string)

IMO, this is a slightly more functional way of doing things...hope it helps

Upvotes: 2

Related Questions