4

I learned early on that there is no reason to use the return keyword in Scala (as far as I'm aware). That being said I found an example where simply changing adding the return keyword made my function work, where it previously didn't.

The code in question comes from my solution to the Advent of Code day 7 challenge.


def containsShinyGoldBag(bagContents: Map[String, List[String]], currentBag: String): Boolean = {
    val contents = bagContents(currentBag)

    if (bagContents(currentBag).contains("shiny gold") ) {
        // Base Case: Bag Found in list of bags
        true
    } else if (contents == List.empty){
        // Base Case: Dead End
        false
    } else {
        // Continue searching down list

        // Ideal solution ( gives same result as the working solution without return keyword )
        // for (b <- contents) containsShinyGoldBag(bagContents, b)

        // Working solution
        for (b <- contents) {
            if (containsShinyGoldBag(bagContents, b)) {
                println(s"Found one! $b inside a $currentBag")
                return true // <--- culprit
            }
            else false
        }
        false
    }
}

// In the main function

var count = 0
for (bag <- bagContents.keys) {
    if (containsShinyGoldBag(bagContents, bag)) {
        count = count + 1
    }
}
println(s"There are $count way to bring a shiny gold bag!")

When I run the code without return I end up with count = 7, which is the number of bags directly containing a shiny gold bag, rather than the correct number which counts bags that contain a shiny gold bag somewhere inside of one of their other bags down the line.

Endzeit
  • 4,810
  • 5
  • 29
  • 52
Zelkins
  • 723
  • 1
  • 5
  • 22
  • 4
    Technically speaking you never ever need it, every algorithm that may need it can be rewriten in a way or another that doesn't need it. - Practically speaking, sometimes the alternative is harder to understand or more inefficient so why not using it? - Personally speaking, I have never found a good reason for using it _(in my own code)_ since it is usually common on more imperative code and mine is usually more functional and things like **cats** provide good alternatives for when you may need it using only the stdlib _(`traverse` being the most common example)_. – Luis Miguel Mejía Suárez Dec 26 '20 at 19:54
  • @LuisMiguelMejíaSuárez I understand that it isn't necessary. I'm just asking what about this specific example makes adding `return` give a different result – Zelkins Dec 26 '20 at 19:56
  • Yeah that is exactly the reason why it is avoided. Because it can mean different things. Sometimes it may mean what you want it to mean _(like this case)_ Sometimes it doesn't. – Luis Miguel Mejía Suárez Dec 26 '20 at 20:05
  • The reason why you need it in this case is because you are using an imperative `foreach` which doesn't return any value as most other Scala expressions. – Luis Miguel Mejía Suárez Dec 26 '20 at 20:06
  • 3
    [return in Scala](https://stackoverflow.com/q/12560463/2359227) – Tomer Shetah Dec 26 '20 at 21:31
  • 2
    The thing about `return` is that it doesn't always work like `return` in, say, Java - non-local returns (from within lambdas and for loops) use an exception to break out and are messy. – user Dec 26 '20 at 22:56

1 Answers1

3

A function returns the value of the last expression it evaluates; in your case that will be one of:

  1. true after if (bagContents(currentBag).contains("shiny gold") );

  2. false after else if (contents == List.empty);

  3. the last false.

true is not in such a position, so you need return to, well, make the function return it. Otherwise it's evaluated and ignored because you don't do anything with it. So is else false in the same for, actually, it can be removed without changing the meaning.

The alternative to avoid return here is

contents.exists(b => containsShinyGoldBag(bagContents, b))
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487