How to determine the return type of a function

Hi Guys,

I am hardware engineer and new to scala. I am working through a guid in the flowing link.

github: chisel-bootcamp/2.3_control_flow.ipynb at master · freechipsproject/chisel-bootcamp · GitHub

I am trying to solve the question about statemap with match…case…

It seems function states return with type Unit. But in the solution, nextstate is assigned to states(…). So it was Int.
I am confused. Why is that?

Codes:


// state map
def states = Map("idle" -> 0, "coding" -> 1, "writing" -> 2, "grad" -> 3)

// life is full of question marks
def gradLife (state: Int, coffee: Boolean, idea: Boolean, pressure: Boolean): Int = {
  var nextState = states("idle")

// my solution below
  nextState = state match {
      case states("idle") => {
          if (idea) states("idle")
          else if (coffee) states("coding")
          else if (pressure) states("writing")
      }
      case states("coding") => {
          if (coffee) states("coding")
          else if (pressure || idea) states("writing")
      }
      case states("writing") => {
          if (coffee || idea) states("writing")
          else if (pressure) states("grad")          
      }
      case states("grad") => states("idle")
  }
/////

  nextState
}

Resutls:


cmd8.sc:7: not found: value states
      case states("idle") => {
           ^cmd8.sc:10: type mismatch;
 found   : Unit
 required: Int
          else if (pressure) states("writing")
               ^cmd8.sc:12: not found: value states
      case states("coding") => {
           ^cmd8.sc:14: type mismatch;
 found   : Unit
 required: Int
          else if (pressure || idea) states("writing")
               ^cmd8.sc:16: not found: value states
      case states("writing") => {
           ^cmd8.sc:18: type mismatch;
 found   : Unit
 required: Int
          else if (pressure) states("grad")          
               ^cmd8.sc:20: not found: value states
      case states("grad") => states("idle")
           ^Compilation Failed

solutions of Guid:

  if (state == states("idle")) {
    if      (coffee) { nextState = states("coding") }
    else if (idea) { nextState = states("idle") }
    else if (pressure) { nextState = states("writing") }
  } else if (state == states("coding")) {
    if      (coffee) { nextState = states("coding") } 
    else if (idea || pressure) { nextState = states("writing") }
  } else if (state == states("writing")) {
    if      (coffee || idea) { nextState = states("writing") }
    else if (pressure) { nextState = states("grad") }
  }

Firstly states is of type Map[String, Int]. Perhaps what you saw was that the “def” statement returned unit, since it was a definition and was not computed.

match is for pattern matching, not checking equality, so case states(idle) => does not make sense. If you want to use match here (I would think if is more natural) you can say case s if s == states(idle) =>

regards,
Siddhartha

Please format your code, this is very difficult to read and places the burden of formatting on the person trying to help you, which isn’t generally going to help your chances of someone being willing to invest their time in helping you.

The root of your problem is that

if (foo) x
else y

is an expression that returns a value of the type of x and y, but that

if (foo) x
else if (bar) y

doesn’t have a result when foo and bar are both false, and returns type Unit. The compiler could maybe have been more informative about it in this case, but unfortunately, it is what it is.

The error message comes from here:

nextState = /* stuff */

nextSate is of type Int, so stuff must also be of type Int. stuff is a match expression, of which all cases should then return a value of type Int. The compiler is telling you they don’t:

cmd8.sc:10: type mismatch;
found : Unit
required: Int
else if (pressure) states(“writing”)
^

That boils down to the point above – this expression is of type Unit since it lacks an else, but an expression of type Int is expected since you want to assign the result of the expression to the Int variable nextState.

that won’t solve all of your problems, unfortunately

case states("grad") => /* ... */

looks reasonable enough, but won’t work: what comes after case can’t be any arbitrary expression, but must be a valid pattern. To keep it close to what you have, you can have something like

state match {
  case s if s == states("idle") => ....
}

If you have it like that, you could for example expand that if with the branch statements following it:

state match {
  case s if s == states("idle") && idea => s
  case s if s == states("idle") && coffee => states("coding")
  ...
  other => other //eventually, we fall through, and don't set a different state
}

in this situation, maybe just using

if (state == states("idle"))
  if (coffee) states("coding")
  else if(pressure) states("writing")
  else state
else if (state == states("writing")
  if ...
...

is the cleanest solution.

or maybe go for

def states = Map("idle" → 0, "coding" → 1, "writing" → 2, "grad" → 3)

val idle = states("idle")
val coding = states("coding")
val writing = states("writing")
val grad = states("grad")

def gradLife (state: Int, coffee: Boolean, idea: Boolean, pressure: Boolean): Int = state match {
  case `idle` if coffee => coding
  case `idle` if pressure => writing
  case `coding` if (pressure || idea) => writing
  case `writing` if pressure => grad
  case `grad` => idle
  case other => other
}

That more or less ignores the Map entirely, of which I’m not sure why the assignment uses it in the first place.

1 Like

Thank you for the long answer. Sorry for the formatting.

It works perfectly with your hint.

I could understand the else branch issue. The guard mode for case is clear as well.
I thought states("idle") should return a value ‘0’ as Int. So

case states("idle") => ...

is equal to

case 1 => ...

I still can’t understand why it was wrong. As case 1 => ... is an example from some text book I have read.
What do you mean by “arbitrary expression”?

You can’t follow case with any expression: case must be followed by a valid pattern. 1 is a valid pattern (specifically, a literal pattern). idle in the example above is also a valid pattern, specifically an identifier pattern, where the pattern is made up of what is called a stable identifier, which can be (some) val's but not defs (see Types for a more precise definition).

states("idle") is not a valid pattern. In a hypothetical scala where it would be a valid pattern, you’d have to determine when exactly that expression is evaluated. In this example it doesn’t really matter, but it would if the expression has any side effects, or takes long to evaluate.

Take, for example, the following hypothetical program where that would work.


def tricky() = {
  println("this is tricky")
  sleep(10 minutes)
  new Radom().nextInt
}


def matchit(i: Int) = i match {
  case tricky() => println("found1")
  case tricky() => println("found2")
  case tricky() => println("found3")
  case _ => println("not found")
}

matchit(7)

matchit(7)


you could imagine a wide range of possible behaviours for this and none of them is very appealing (though also not that horrible I suppose).

1 Like

So it is because the states() does not have a clear return type of Int.
I think I got it, thank you.

states("foo") does have a clear return type of Int. You can’t use it in a match statement, because a match statement needs a valid pattern behind a case statement, and states("foo") isn’t a valid pattern.

Valid patterns are listed at Pattern Matching | Scala 2.13

You could imagine scala allowing it as a pattern which matches a value if it evalues to the same value under == equality, I’m sure the difficulties you’d run in to are quite tractable, but that’s not the language scala is at the moment.

1 Like