Pattern match in foreach and then do a final step

13,623

Solution 1

I don't know what your ctm is, but you could always do this:

val xs = List("date", "since", "other1", "other2")

xs.foreach { str =>

    str match {
        case "date"  => println("Match Date")
        case "since" => println("Match Since")
        case unknow  => println("Others")
    } 

    println("Put your post step here")
}

Note you should use {} instead of () when you want use a block of code as the argument of foreach().

Solution 2

I will not answer your question, but I should note that reassigning variables in Scala is a bad practice. I suggest you to rewrite your code to avoid vars.

First, transform your strings to something else:

val strings = it map {
  case "date" => "Current date: " + new Date().toString + "<br/>"
  case "since" => "Last command executed: " + (ctm - last) + "ms before now<br/>"
  case unknow => "Command: " + unknown + " not recognized <br/>"
}

Next, push it

strings map { out.push(_) }

It looks like your implementation of push has side effects. Bad for you, because such methods makes your program unpredictable. You can easily avoid side effects by making push return a tuple:

def push(s: String) = {
  ...
  (ctm, last)
}

And using it like:

val (ctm, last) = out.push(str)

Update:

Of course side effects are needed to make programs useful. I only meant that methods depending on outer variables are less predictable than pure one, it is hard to reason about it. It is easier to test methods without side effects.

Yes, you should prefer vals over vars, it makes your program more "functional" and stateless. Stateless algorithms are thread safe and very predictable.

It seems like your program is stateful by nature. At least, try to stay as "functional" and stateless as you can :)

My suggested solution of your problem is:

// By convention, methods with side effects takes an empty argument list
def ctm(): Long = // Get current time

// Command handlers
def date() = "Current date: " + new Date().toString + "<br/>"
def since(last: Long) = "Last command executed: " + (ctm() - last) + "ms before now<br/>"
def unknown(cmd: String) = "Command: " + unknown + " not recognized <br/>"    

  // In your cmd processing loop

  // First, map inputs to responses
  val cmds = inps map {
    case "date"  => date()
    case "since" => since(last)
    case unk     => unknown(unk)
  }

  // Then push responses and update state
  cmds map { response =>
    out.push(response)
    // It is a good place to update your state
    last = ctm()
  }

It is hard to test this without context of your code, so you should fit it to your needs yourself. I hope I've answered your question.

Share:
13,623
Farmor
Author by

Farmor

Updated on June 27, 2022

Comments

  • Farmor
    Farmor almost 2 years

    Is it possible to do anything after a pattern match in a foreach statement?
    I want to do a post match step e.g. to set a variable. I also want to force a Unit return as my foreach is String => Unit, and by default Scala wants to return the last statement.

    Here is some code:

        Iteratee.foreach[String](_ match {
          case "date" => out.push("Current date: " + new Date().toString + "<br/>")
          case "since" => out.push("Last command executed: " + (ctm - last) + "ms before now<br/>")
          case unknow => out.push("Command: " + unknown + " not recognized <br/>")
        } // here I would like to set "last = ctm" (will be a Long) 
        ) 
    

    UPDATED: New code and context. Also new questions added :) They are embedded in the comments.

    def socket = WebSocket.using[String] { request =>
    
     // Comment from an answer bellow but what are the side effects?
     // By convention, methods with side effects takes an empty argument list
     def ctm(): Long = System.currentTimeMillis
    
     var last: Long = ctm
    
     // Command handlers
     // Comment from an answer bellow but what are the side effects?
     // By convention, methods with side effects takes an empty argument list
     def date() = "Current date: " + new Date().toString + "<br/>"
     def since(last: Long) = "Last command executed: " + (ctm - last) + "ms before now<br/>"
     def unknown(cmd: String) = "Command: " + cmd + " not recognized <br/>"
    
     val out = Enumerator.imperative[String] {}
    
     // How to transform into the mapping strategy given in lpaul7's nice answer.
     lazy val in = Iteratee.foreach[String](_ match {
       case "date" => out.push(date)
       case "since" => out.push(since(last))
       case unknown => out.push(unknown)
     } // Here I want to update the variable last to "last = ctm"
     ).mapDone { _ =>
       println("Disconnected")
     }
    
     (in, out)
    }