2014-01-28 3 views
0

У меня есть (Akka) актер код, используя тематический класс + конструктор копирования для обновления состояния:Scala pass function args через конструктор экземпляра класса case?

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(...)) 
    } 
} 

Я хотел бы добавить ниже импорта

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

так, что код может быть

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

но это не скомпилировано. Я могу, конечно, настроить def update немного, чтобы избавиться от большинства шаблонного, но copy до сих пор торчит вокруг:

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 
    } 
} 

Есть ли сопоставимый синтаксис, который позволит мне пройти через арг конструктору случай класс копирования и высушить этот последний бит?

ответ

0

Отказ от ответственности: Я думаю, лучшим решением является использование context become явно. И я не рекомендую вам использовать приведенный ниже код.

Я думаю, что это невозможно без метапрограммирования (макросов). Вы должны создать метод со значениями по умолчанию для именованных параметров.

Вы всегда можете создать такой метод вручную, как это:

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

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

Но я предполагаю, что это не то, что вы хотите.

Единственный способ получить такой метод без метапрограммирования - это использовать метод copy. Метод copy вызывает конструктор, и вы можете вызвать become в конструкторе.

Код ниже работает, но я сильно не рекомендую вам его использовать! Это криптокод, и это запутает всех других разработчиков.

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") 
} 

Использование:

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) 
+0

hrrrm, я думаю, что я согласен с отказом - Я ищу, чтобы сделать код базы более (не) разработчик понятной, а не другая вещь. Тем не менее, мне любопытно: как выглядит этот подход, если есть несколько полей одного типа? например, 'Behavior (intOne: Int, intTwo: Int, str: String)'? – Carl

+0

@ Карл: Я не знал, что это было так запутанно. Исправлено, см. Обновление. – senia

Смежные вопросы