Scala pass function args through to case class copy constructor?

I have some (Akka) actor code that is using a case class + the copy constructor to update state:

def foo(state:StateCaseClass) : Receive = {
  import state._

  {
    case Bar(updates) =>
      context become foo(copy(/* change a limited number of things */))
    // ... other message processing w/ lots of context become foo(copy(...))
  }
}

I'd like to add below the import

def update = context become foo(copy(_))

so that the code can be

def foo(state:StateCaseClass) : Receive = {
  import state._
  def update = context become foo(copy(_))
  {
    case Bar(updates) =>
      update(/* change a limited number of things */)
    // ... etc
  }
}

but that doesn't compile. I can of course tweak the def update a bit to get rid of most of boilerplate, but the copy still sticks around:

def foo(state:StateCaseClass) : Receive = {
  import state._
  def update(newState:StateCaseClass) = context become foo(newState)

  {
    case Bar(updates) =>
      update(copy(/* change a limited number of things */))
    // ... etc
  }
}

Is there comparable syntax that will let me pass through the args to the case class copy constructor and dry out that last bit?

Answers


Disclaimer: I guess the best solution is to use context become explicitly. And I don't recommend you to use the code below.

I guess it's impossible without metaprogramming (macros). You have to create a method with default values for named parameters.

You could always create such method manually like this:

def update(filed1: Int = state.field1, field2: String = state.field2) =
  context become foo(StateCaseClass(filed1, filed2))

...
  update(field1 = 0)
...
  update(field2 = "str")

But I guess it's not what you want.

The only way to get such method without metaprogramming is... to use method copy itself. Method copy calls constructor and you could call become in constructor.

The code below works, but I strongly don't recommend you to use it! It's a cryptocode and it will confuse all other developers.

import akka.actor._

trait ReceiveHelper extends PartialFunction[Any, Unit] {
  def receive: PartialFunction[Any, Unit]
  override def apply(v: Any) = receive(v)
  override def isDefinedAt(v: Any) = receive isDefinedAt v
}

sealed trait TestActorMessage
case object Get extends TestActorMessage
case class SetInt(i: Int) extends TestActorMessage
case class SetString(s: String) extends TestActorMessage

class TestActor extends Actor {
  case class Behaviour(intField: Int, strField: String) extends ReceiveHelper {
    context become this

    val receive: Receive = {
      case Get => sender ! (intField -> strField)
      case SetInt(i) => copy(intField = i)
      case SetString(s) => copy(strField = s)
    }
  }

  def receive = Behaviour(0, "init")
}

Usage:

val system = ActorSystem("testSystem")
val testActor = system.actorOf(Props[TestActor], "testActor")

import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

implicit val timeout = Timeout(5 seconds)

testActor ? Get foreach println
// (0,init)

testActor ! SetInt(666)

testActor ? Get foreach println
// (666,init)

testActor ! SetString("next")

testActor ? Get foreach println
// (666,next)

Need Your Help

sails.js v0.11.0 assets not being copied

gruntjs sails.js

I'm new to node.js, sails.js and grunt.js.

How to Add Column in Final Excel file from merged excel files

excel vba excel-vba excel-2010

I currently have this excel macro below that basically merging all files indicated in the path into one excel file.