Number guessing in scala


#1

Hi community,

I am new to scala and I am trying to get used to various concepts of scala. One of them seems to be to always prefer val over var. This seems to imply that loops are replaced by recursions. I have written a simple game of guessing numbers. I have written it using two attempts: attempt1 is how I would have written it in a language such as Java and attempt2 is the way I would do it in Scala. Now I am asking some people with more experience than me: is attempt2 the scala way to do it? Or am I missing some beautiful concepts here? :wink:

import scala.io.StdIn._
import scala.util.Random

object Main extends App {
  val TRIALS = 3

  def attempt1(): Unit = {
    val numberToGuess = new Random().nextInt(100)
    var guessedNumber = -1
    var trials = 0

    println("Guess a number between 0 and 100")
    // println(numberToGuess)
    while (trials < TRIALS && guessedNumber != numberToGuess) {
      print("Your guess?: ")
      guessedNumber = readInt()
      if (guessedNumber > numberToGuess) println("Too large!")
      else if (guessedNumber < numberToGuess) println("Too small!")
      else {
        println("You got it!")
        return
      }
      trials += 1
    }
    println("You lost")
  }

  def attempt2(): Unit = {
    def singleGuess(numberToGuess: Int, trials: Int = TRIALS): Boolean = {
      if (trials == 0) return false

      val guessed = { print("Your guess?: "); readInt() }

      if (guessed > numberToGuess) { println("Too large!"); singleGuess(numberToGuess, trials-1) }
      else if (guessed < numberToGuess) { println("Too small!"); singleGuess(numberToGuess, trials-1) }
      else { println("You got it!"); return true }
    }

    val numberToGuess = new Random().nextInt(100)
    println("Guess a number between 0 and 100")
    // println(numberToGuess)
    if (!singleGuess(numberToGuess)) {
      println("You lost")
    }
  }

  // attempt1()
  attempt2()

}

#2

I wouldn’t say there is one Scala way to do things like this. It mostly comes down to how much of a functional programming style you want to adopt. Not using var is about programming in a functional style, and although Scala definitely aims to support that and encourages immutability, it does not enforce it like some pure functional languages (e.g. Haskell).

Therefore the rest of my answer will be about how to adapt a more functional style (
NB: I lecture a functional programming course, so I’m biased :wink: )

From a functional point of view, recursion is used instead of loops with vars, because assigning a new value to a var is a side effect. But so are user input and console output, which you both use in your recursive function. As your program is pretty small and mostly consists of user interaction (= I/O, so, a side effect), there is not much to separate out into pure (= side effect free) code. I’d say both are valid solutions.

Also your loop is kind of a special case, as it is a “run n times or until a condition”. In my experience, most loops in imperative code are over some kind of collection, like a List. Those kinds of loops are different, because they often don’t really care about the index variable, but about the element at that index. If you come from a Java background, you’ll probably already have seen foreach loops (e.g. for(Elem x : collection) ...). This is already no longer using a variable, as x is scoped to the method body, and practically each run of the body gets its own x, not a reassigned x. The scala equivalent to that are for-comprehensions, which under the hood use functions on the collection like map and filter, to run operations on each element.

One thing that can easily be improved in your second attempt is not using return. An explanation of why it is bad for FP would be longer, but regardless of that, it is not that fast. A return in Scala is actually implemented as throwing a special exception. You can always replace an if(x) return foo by if(x) foo else {...}. Remember that if in Scala is an expression and returns a value, and a function returns the value of its last expression. Your method would then look like:

def singleGuess(numberToGuess: Int, trials: Int = TRIALS): Boolean = {
  if (trials == 0)  false
  else {
    val guessed = { print("Your guess?: "); readInt() }
 
    if (guessed > numberToGuess) { println("Too large!"); singleGuess(numberToGuess, trials-1) }
    else if (guessed < numberToGuess) { println("Too small!"); singleGuess(numberToGuess, trials-1) }
    else { println("You got it!"); true }
  }
}

Other than that, your solution is fine for beginning with Scala. Applying a functional style to IO is possible with advanced concepts (IO monads) to encapsulate IO in a functional way, but lots of Scala code is not built in such a strictly functional way. As Scala allows for pure and non-pure code, you don’t have to fully commit to “do everything in a FP way” and still have benefits from the parts where it is easier to apply. A few tips to get started:

  • You can start with replacing loops over collections with map (apply a function to each element of a collection and return a new collection) and filter (remove elements not satisfying a condition), where you know how. When you encounter loops, that you can not replace that way, look at the other functions your collection provides. You will develop a feeling for when which function is appropriate over time. Some of those are explained in Oderskys FP course on Coursera, which I can recommend as an Intro to Scala in general.

  • Start with avoid vars in a larger scope, e.g. class fields, and feel free to use vars local to a single function, until you have some experience in using immutable data structures.

  • Look for parts of your code, that do not directly need to do IO, but can be put into a function receiving the input as a parameter and returning output. For example when reading in a file, separate the interpretation of the file contents into a separate function that gets passed the contents, so it does not care about them being from a file.

Most of these are not scala-specific, but can be applied to a lot of programming languages.

Regards
Alexander Gehrke


#3

Just one small thing I’d add to @crater2150’s excellent answer:

val TRIALS = 3

When something is intended to be a constant like this, I’d typically declare it as final val. That means that it can’t be overridden in a child class, and allows the compiler to use it more efficiently. (In Scala 3, this looks likely to become especially powerful for some use cases.)

Also, I’d like to underline this point:

I think the average Scala programmer is fairly sanguine about using a local var in a smaller function: so long as the context is small enough to easily reason about what’s going on accurately, it doesn’t usually do much harm. I don’t do it often, but once in a while it’s the clearest way to express what I want to do.

What you want to avoid is letting vars leak into the larger world, which can quickly make things much harder to reason about. That’s why var fields are a fairly strong anti-pattern, while local function vars are rather less dangerous.


#4

For a stateful, mutable object, which would you choose?

private val data: scala.collection.mutable.Map[A, B]
private var data: scala.collection.immutable.Map[A, B]

The var approach has some benefits, including the fact that you can freely share references to data with other code (e.g., a getSnapshot method can afford to return a reference to data). I’m not saying vars are good, just that they can allow you to use persistent data structures in some cases, which has its own advantages.

MC


#5

There’s no one-size-fits-all answer – you have to be extremely conscious of the usage of the enclosing class, particularly its threading implications. (In a single-threaded environment such as JS, var members are less dangerous, although they still tend to make code harder to reason about.) In general, I try to avoid stateful, mutable objects where practical, because they are always prone to giving you headaches.

To your specific example: yes, a var containing an immutable value is typically less-bad than a val containing a mutable one – but only if the enclosing class is designed in such a way as to make that safe.

Honestly, I actually use this sort of var quite often – but mainly as the top-level state member for an Akka Actor. Since the Actor itself is guaranteed to be used (effectively) single-threaded, it’s safe for it to contain a var, and since I try to follow the read-evaluate-write pattern for Akka Actors, it’s reasonably easy to reason about. (Processing an individual message is effectively the “end of the world”.) As you say, since the value is immutable, it can safely be shared elsewhere, which is a big win.

I also often allow myself a few vars in my system singletons, but again those are specialized situations: they get set up during system initialization (as sub-systems are registering with each other), which I keep largely single-threaded, and they are immutable once the application is running.

Mind, “anti-pattern” doesn’t mean “you can never do this, ever”. It means that var fields are a bit of a hand grenade: you should only pull the pin if you know exactly where you’re throwing it…


#6

Then I would not call it an “anti-pattern”.

“An anti-pattern is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive.” (Wikipedia)


#7

I’m not particularly interested in getting into a semantics argument, but I disagree with the Wikipedia definition. In my experience, anti-patterns usually get the job done – they just have enough downsides that they should usually be avoided…


#8

I like the hand grenade analogy.