Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala local return?

I've just discovered that returns in following closure will return from function findPackage

def findPackage(name: String, suffix: Option[String] = None): Path = {
    logger.debug("Looking for package {} with suffix {}", name, suffix)
    val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
        for (val path <- dirs) {
            val matcher = packagePattern.matcher(path.getFileName.toString)
            if (matcher.matches() && matcher.group(1).equals(name))
                if (suffix.isDefined) {
                    if (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
                        return path
                } else
                    return path
        }
        throw new PackageNotFoundException(this, name, suffix)
    }
    logger.debug("Found package is {}", path)
    path
}

Can I somehow do a local return please? Thank you.

like image 469
woky Avatar asked Nov 30 '22 03:11

woky


2 Answers

Or you could get rid of your loop and replace it with what you're trying to do: "find"

def findPackage(name: String, suffix: Option[String] = None): Path = {
    logger.debug("Looking for package {} with suffix {}", name, suffix)

    def matching(path : Path) : Boolean = {
        val matcher = packagePattern.matcher(path.getFileName.toString)
        matcher.matches && matcher.group(1).equals(name) && (!suffix.isDefined || (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
    }

    val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
       dirs find matching getOrElse {throw new PackageNotFoundException(this, name, suffix)}
    }

    logger.debug("Found package is {}", path)
    path
}
like image 200
James Iry Avatar answered Dec 05 '22 11:12

James Iry


I fully support James Iry's suggestion, but for the sake of demonstration:

def findPackage(name: String, suffix: Option[String] = None): Path = {
    logger.debug("Looking for package {} with suffix {}", name, suffix)
    val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
        try {
          for (val path <- dirs) {
              val matcher = packagePattern.matcher(path.getFileName.toString)
              if (matcher.matches() && matcher.group(1).equals(name))
                  if (suffix.isDefined) {
                      if (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
                          return path
                  } else
                     return path
          }
          throw new PackageNotFoundException(this, name, suffix)
        } catch { case e:scala.runtime.NonLocalReturnControl[Path] => e.value}
    }
    logger.debug("Found package is {}", path)
    path
}

What changed?

I have added a try{} block around the body of the anonymous function and then catch expression at the end looking for scala.runtime.NonLocalReturnControl exception, then I extract and pass on the return value.

Why it works?

Returning from a nested anonymous function raises scala.runtime.NonLocalReturnControl exception which is caught by the host function or method.

Scala Language Spec, section 6.20 Return Expressions:

... Returning from a nested anonymous function is implemented by throwing and catching a scala.runtime.NonLocalReturnException. Any exception catches between the point of return and the enclosing methods might see the exception. A key comparison makes sure that these exceptions are only caught by the method instance which is terminated by the return.

If the return expression is itself part of an anonymous function, it is possible that the enclosing instance of f has already returned before the return expression is executed. In that case, the thrown scala.runtime.NonLocalReturnException will not be caught, and will propagate up the call stack.

like image 24
Vlad Gudim Avatar answered Dec 05 '22 09:12

Vlad Gudim