Scalera tip: Keep your actor’s state with no VAR at all!

It’s pretty well known that using VARs is, apart from unethical, the evil itself, some kind of hell, it make kitties die and many other stuff you might probably heard before and that could eventually be the cause of a painfull slowly dead.

The essence of functional programming is therefore the immutability: every time I mutate an element, I actually generate a new one.

What about Akka actors?

When we talk about actors, we can define them as stateful computation units that sequentially process a message queue by reacting(or not) to each of these messages.

It’s always been said that, in order to keep state within some actor’s logic, it was ok to use VARs:

It’s impossible that concurrency problems happen: it’s the actor itself and nobody else who access that var and will only process one message at a time.

But maybe, we could renounce to this premise if we look for some way to redefine the actor’s behavior based on a new state.

Mortal approach

If we follow the previously described philosophy, the very first (and more straight forward) approach for keeping some actor’s state would be pretty similar to the following:

class Foo extends Actor{
  var state: Int = 0
  override def receive = {
    case Increase => state += 1
  }
}

Every time an Increase arrives, we modify the state value by adding 1.
So easy so far, right?

Immutable approach

Nevertheless, we could define a receive function parameterized by certain state, so when a message arrives, this parameter is the state to take into account.

If the circumstances to mutate the state took place, we would just invoke the become method that would modify the actor’s behavior. In our case, that behavior mutation would consist on changing the state value.

If we use the previously defined example:

class Foo extends Actor{
  def receive(state: Int): Receive = {
    case Increase =>
      context.become(
        receive(state + 1),
        discardOld = true)
  }
  override def receive = receive(0)
}

we can notice that the function defined by receive is parameterized by some state argument. When some Increase message arrives, what we perform is an invocation to become for modifying the actor’s behavior, passing as an argument the new state to handle.

If we wanted some extra legibility, we could even get abstract from every updatabe-state actor:

trait SActor[State] extends Actor {
  val initialState: State
  def receive(state: State): Receive
  def update(state: State): Receive =
    context.become(
      receive(state),
      discardold = true)
  override def receive =
    receive(initialState)
}

this way, we just have to define the initial state of some actor, a new parameterized receive function and a new update function that takes care of performing the proper become call as explained before.
With all these in mind, we now have some cuter brand new Foo actor:

class Foo extends SActor[Int] {
  val initialState = 0
  def receive(state: Int): Receive = {
    case Increase => update(state +1)
  }
}

Potential hazardous issues

Please, do notice that in the featuring example, we’ve used a second argument for become: discardOld = true. This argument settles whether the new behavior should be stashed on the top of the older one, or ‘au contraire’ it should completely substitute the previous behavior.

Let’s suppose we used discardOld = false. If every single time a new Increase message arrived we had to stash a new behavior, we could obtain a wonderful overflow issue.

See you in the next tip.

Peace out ūüôā

Scalera tip: Mant√©n estado en tu actor sin usar un solo VAR

Es de todos sabido que usar VARs es algo que, aparte de mal visto, está mal, es el infierno en vida, hace que mueran gatitos y muchas otras perlitas que probablemente ya habréis oído antes y que por poco os ha causado una muerte lenta y dolorosa en el cadalso.

La esencia en programación funcional es, por tanto, la inmutabilidad: cada vez que muto un elemento, genero uno nuevo.

What about Akka actors?

Cuando hablamos de actores, podemos definirlos como unidades con estado que procesan de manera secuencial una cola de mensajes, asociando (o no) a cada uno de estos mensajes una cierta lógica computacional.

Siempre se ha dicho que para mantener dicho estado dentro de la lógica de un actor, no pasaba nada si usabas un var:

Es imposible que hayan problemas de concurrencia: solo el propio actor tiene acceso a dicho VAR y procesar√° un solo mensaje al mismo tiempo.

Pero quiz√°s podamos renunciar a esta premisa si buscamos una manera de redefinir el comportamiento del actor en base a un nuevo estado.

Mortal approach

Siguiendo la filosofía antes descrita, la primera (y más directa) aproximación para mantener el estado en un actor se parecería bastante a lo siguiente:

class Foo extends Actor{
  var state: Int = 0
  override def receive = {
    case Increase => state += 1
  }
}

Cada vez que llega un mensaje Increase, modificamos el valor de state, sumando 1.
Hasta aqu√≠ nada complicado, ¬Ņno?

Immutable approach

Sin embargo, podríamos definir una función receive que estuviera parametrizada por un cierto estado, de manera que cuando llegue un mensaje, el estado a tener en cuenta sea este parámetro.

Si se diera la circunstancia de tener que actualizar el valor de dicho estado, bastaría con invocar al método become que modifica el comportamiento del actor. En nuestro caso, dicha modificación del comportamiento consistiría en cambiar el valor del estado.

Si usamos el mismo ejemplo que antes:

class Foo extends Actor{
  def receive(state: Int): Receive = {
    case Increase =>
      context.become(
        receive(state + 1),
        discardOld = true)
  }
  override def receive = receive(0)
}

vemos que la función que define el receive en base al estado recibe un parámetro denominado state. Cuando llega un mensaje de tipo Increase, lo que hacemos es invocar a become para modificar el comportamiento del actor, pasando como argumento el nuevo estado a tener en cuenta.

Si queremos mejorar un poco la legibilidad, podríamos incluso abstraer todo actor con estado actualizable:

trait SActor[State] extends Actor {
  val initialState: State
  def receive(state: State): Receive
  def update(state: State): Receive =
    context.become(
      receive(state),
      discardold = true)
  override def receive =
    receive(initialState)
}

de manera que se especifique el estado inicial del actor, una nueva función de receive que queda parametrizada por el nuevo estado a gestionar, y una nueva función de update que se encarga de realizar la llamada a become como antes explicábamos.
Con todo ello nos queda un nuevo actor Foo mucho m√°s curioso:

class Foo extends SActor[Int] {
  val initialState = 0
  def receive(state: Int): Receive = {
    case Increase => update(state +1)
  }
}

Potential hazardous issues

Nótese que en el ejemplo de antes hemos pasado un segundo argumento: discardOld = true. Este argumento indica si el comportamiento nuevo debe apilarse sobre el anterior o si por el contrario debe sustituirlo por completo.

Supongamos que usáramos un discardOld = false. Si cada vez que llegase un mensaje de incremento, apilásemos un nuevo comportamiento, podríamos llegar a tener un problema de desbordamiento.

Hasta el próximo briconsejo.

Agur de lim√≥n ūüôā

Scala: One language to rule them all (I)

In the beginning, there were not programming languages, just machine code. Automatic programming came to save all us by casting power rings to rule the machines: Programming languages. They diversified, inspired new ones, constituted a rich ecosystem. Among them appeared a highly useful kind of computer languages: DSLs.

DSLs

Domain Specific (computer) Languages have been around for¬†quite a long time. The academic description of this kind of languages states that they are centred and used for an specific application domain. DSLs¬†are¬†small¬†and¬†concise¬†which implies that they guide their users in the process of describing actions, entities or relations within their domain of application. What’s more important, they are made to fit¬†their purpose.

If I am doing statistics research, why should worry about memory management?!!

It isn’t surprising that a whole kingdom of these mini-languages has evolved and took over¬†machine learning tools, scientific programming, hardware design and management, modeling…

Wasn’t this a blog on Scala? Leave the history for academics!

Yeah this might sound as the stories from the past. However, in the context of the current times, when we all seem¬†obsessed by data acquisition, storage and analysis and taking into account that¬†this data is usually complex to manage because of its variability. We are forced to deal with dozens of different ways of managing data and many of them pass through the use of DSLs: SQL, HiveQL, CQL, bash, grep, awk, R, C’mon! How can I even finish the list! Let’s forget of¬†what is to come.

What if a tool would give as the power to create simple, guided and short languages to perform specific domain tasks within.  What if Scala was a kind of DSL where the D stands for the Domain of Creating new DSLs?!

When its creators named Scala they were not just thinking on its capabilities in code reuse and  potential use in horizontal concurrent environments; they also kept in mind the extensibility of the language. Some of the features in that direction are:

  • Infix notation: objectA.method(objectB) can be written as objectA method objectB
  • Lack of operators: There are no operators as separated entities, just methods. Precedence order and associativity rules are provided by the last character of each method name.This way, any method name NOT ending with¬†`:` gets associated from left to right:
    obj1 + obj2 is the same as writing obj1.+(obj2) whereas obj1 +: obj2 is as writing obj2.+:(obj1).
    Similarly, operator precedence is provided by a priority list of method name ending characters. e.g: `*` gets a higher priority than `+`, obj1 + obj2 * obj3  is always interpreted as obj1 + (obj2 * obj3).The mentioned precedence priority list is as follows:

    • Any letter, no matter case.
    • |
    • ^
    • &
    • Symbols¬†= and !
    • Symbols¬†< and >
    • :
    • Arithmetic operations¬†+ and –
    • Arithmetic operations¬†¬†*, / and %
    • Any other special character.
  • Advanced object oriented features:¬†object, trait, …

These features can be combined to model and build internal DSLs within the Scala programming language.

Scala DSLs 101

Infix notation is the main feature to create our own embedded languages.

Consider the following trait:

trait MovingRobot {
  def moveForward(): MovingRobot = {
    println("Robot moved one position forward")
    this
  }
  def moveBackward(): MovingRobot = {
    println("Robot moved one position backward")
    this
  }
}

Which can be mixed in an object declaration as:

object robot extends MovingRobot

Its methods can be called using traditional dot notation:

robot.moveForward.moveBackward

But infix notation give us a more natural way to talk with this simple bot:

robot moveForward() moveBackward
robot moveForward
robot moveBackward

This is the simplest  of all possible DSLs.

State transitions

Yes, simple but rather imperative and useless.  Commands are not changing the system state besides the lateral effect behind println:

simpliest_automaton

At this point, there are two options to model the effects of the DSL instructions:

  • The mutable approach: Somehow easiest to Scala¬†new comers from imperative languages but it is way more bug prone. This one is rather similar to the approach followed by so many builders in Java. Check Java’s StringBuilder:

    The builder state is the string that is being composed. Methods, such as append(double d), return a reference to the instance of the builder whose state has been altered by the very same method.  Hence, the same reference is always returned since is the same StringBuilder instance which is mutating call after call, sounds familiar?!mutable_state
  • The immutable one (or the path of wisdom): Do not change anything, return a new state with the attributes¬†derived from the previous state your action. From now, this post will only cover this approach.

The beauty of the second solution is that each action returns a new state object having a 1 to 1 relation with the system state. That is, the code entities are a perfect reflection of all the changes. Moreover the state is immutable by definition.

state¬†/ste…™t/¬†¬†n., adj., v.,¬†stat‚ÄĘed, stat‚ÄĘing.¬†
n.

  1. the condition of a person or thing with respect to circumstances or experiences;
    the way something is[countable; usually singular]the state of one’s health.
  2. the condition of substances with respect to structure, form, etc.[countable]Water in a gaseous state is steam.

(www.wordreference.com)

Discussing why immutability drives to way less buggy systems is out of the scope of this post, hundreds¬†of explanations can be found by googling “immutability reduces bugs”. Even Java creators decided it was better, at least for their strings.

state_transition

Each transition returning a whole new state reduces its responsibility  to just one: To generate a new state hence simplifying the DSL design. No changes in the state are to be expected beyond explicit transition calls.

The nitty-gritties of immutable state transitions

Following the utterly complex example of our uni-dimensional robot API (at this point you must have realized that the previous Scalera Challenge included a beautiful DSL), it can be altered to make it follow the above-described functional approach:

// All states extend `RobotState`
trait RobotState {
  def position: Int
}

// Transitions which can be mixed with any state for which they
// make sense.

trait MovementTransitions {
  self: RobotState =&amp;gt;

  def moveForward(nSteps: Int = 1): RobotState with MovementTransitions

  def moveBackward(nSteps: Int = 1): RobotState with MovementTransitions

}

// States
// In this example, states only differ in the robot position so they all
// are represented by the same case class.
case class Robot(position: Int) extends RobotState with MovementTransitions {

  def moveForward(nSteps: Int = 1) =
    Robot(position + nSteps)

  def moveBackward(nSteps: Int = 1) =
    Robot(position - nSteps)

}

// Initial state
val robot = new Robot(0)

And its use:

robot moveForward(10) moveBackward() position

The code above is an oversimplification but shows the basic tricks behind Scala DSLs, namely: The use of infix notation, families of states and transitions only usable within state definitions.

A bit of theory: Really? State Machines?

Is the state machine model actually needed to implement DSLs? Yes, if you like avoiding shooting yourself in the foot.

shootfoot

Immutable state machines are easy to understand, maintain and expand.

On the other hand, DSLs are languages, formal languages with grammars with a place in¬†Noam Chomsky’s classification,¬†commonly, Regular Grammars and Context-Free grammar.

  • Which theoretical machine is able to recognize/process languages with regular grammars? A finite state automaton
  • In the case of Context-Free grammar languages, they can be processed by push-down automatons¬†which (ALERT! Oversimplification ahead) can be regarded as a finite automaton enjoying the perk of making use of its own stack to place and read symbols.

The transition model described afore¬†seems to be just made for implementing this kind of machines. A self-answered question arises as to whether DSLs’ developers should dedicate their efforts to find buggy and flimsy solutions when such a solid model is available.

– Well, I suggest you gentlemen invent a way to put a square peg in a round hole.

gene_krantz

– Sir, we have plenty of round pegs!

engineer

Coming soon…

In the next episode: A practical DSLs development step by step, no more history, theory or that silly things nobody cares about! Just…

letsdoit

Scala: Un lenguaje para gobernarlos a todos (I)

Al principio, no ten√≠amos lenguajes de programaci√≥n, s√≥lo c√≥digo m√°quina. Entonces, la¬†programaci√≥n autom√°tica¬†vino para salvarnos a todos. Proporcionando anillos de poder para controlar a las m√°quinas: Estos fueron llamados “Lenguajes de programaci√≥n”. Se diversificaron, sirvieron de inspiraci√≥n para otros nuevos y acabaron constituyendo un rico ecosistema. De entre ellos surgi√≥ un nuevo y √ļtil g√©nero de lenguajes para ordenadores: Los DSLs.

DSLs

Los lenguajes de prop√≥sito espec√≠fico,¬†Domain Specific (computer) Languages,¬†existen desde hace bastante tiempo. La definici√≥n acad√©mica para este sub-grupo establece que han de estar enfocados en el marco de un dominio concreto de aplicaci√≥n. Los DSLs son peque√Īos y concisos, esto implica que son de gran ayuda ya que gu√≠an a sus usuarios a trav√©s del proceso de describir acciones, entidades y relaciones dentro de su dominio de aplicaci√≥n. A√ļn m√°s importante es el hecho de que est√°n hechos y dise√Īados para servir a un √ļnico prop√≥sito.

Si soy un matem√°tico y estoy haciendo un estudio¬†estad√≠stico con mi ordenador¬†¬ŅPor qu√© deber√≠a preocuparme acerca de la¬†gesti√≥n de memoria?

No es de sorprender que haya surgido un gran reino de mini-lenguajes que han tomado el poder sobre actividades teles como el aprendizaje autom√°tico, la programaci√≥n cient√≠fica, el dise√Īo y gesti√≥n de hardware, modelado de datos, …

¬°Y a mi qu√©! ¬ŅNo era esto un blog sobre Scala?

¬°Cierto! todo esto puede sonar¬†a antiguas batallitas. Sin embargo, en una actualidad en la que todos parecemos estar obsesionados por la adquisici√≥n, almacenamiento y an√°lisis de grandes vol√ļmenes de datos que son, a su vez, de naturaleza muy variable (o lo que es lo mismo, mezclando peras con manzanas); Estamos obligados a enfrentarnos a docenas de formas de tratar estos datos y muchas de estas formas no son m√°s que casos concretos de DSLs: SQL, HiveQL, CQL, bash, grep, awk, R… !Por favor! !Cuando va a acabar esta lista! Probablemente: Nunca. Y eso que estamos obviando lo que est√° por venir.

Que maravilla si hubiese una herramienta que nos ayudase, pobres humanos, a construir DSLs, un meta-DSL que, de alguna manera, nos guiase a la hora de desarrollar nuevos DSLs. Esta herramienta existe, y se llama Scala.

Cuando los creadores de este gran (no sólo por sus bondades sino por su extensión) lenguaje de programación le pusieron nombre, no sólo lo hicieron pensando en su capacidad para la re-utilización de código y potencial uso en entornos concurrentes con facilidad para escalar horizontalmente, sino que también tenían en mente su potencial para ser fácilmente expandido. Quizás sea esa la razón de su basta extensión en sintaxis y herramientas. Algunas de las características de Scala en la dirección de la expansibilidad son:

  • Notaci√≥n infija:¬†Cualquier llamada a un m√©todo que recibe un objeto como par√°metro puede ser escrita de forma infija. Esto es,
    objA.metodo(objB) puede escribirse como objA metodo objB.
  • No existencia de operadores: A diferencia de otros lenguajes, carece de¬†operadores como elementos con entidad propia. Todo son m√©todos cuyas reglas de precedencia y¬†asociatividad est√°n determinadas por el √ļltimo car√°cter del nombre del m√©todo.As√≠, cualquier m√©todo cuyo nombre no acabe en el caracter `:` asocia de izquierda a derecha:¬†obj1 + obj2¬†es lo mismo que¬†obj1.+(obj2)¬†en tanto que¬†obj1 +: obj2¬†es lo mismo que¬†obj2.+:(obj1).
    Algo parecido ocurre con la precedencia ya que existe un orden de importancia de caracteres finales, un ejemplo es la mayor importancia de `*` frente a `+` :
    obj1 + obj2 * obj3  
    es siempre igual a obj1 + (obj2 * obj3).Esta es la lista de importancia (de menor a mayor prioridad):

    • Todas las letras del alfabeto, en may√ļsculas o min√ļsculas.
    • |
    • ^
    • &
    • Los s√≠mbolos = y !
    • Los s√≠mbolos < y >
    • :
    • Las operaciones aritm√©ticas¬†+ y –
    • Las operaciones aritm√©ticas *, / y %
    • Cualquier otro caracter especial.
  • Caracter√≠sticas avanzadas de programaci√≥n orientada a objetos: object, trait, …

 

Estas tres características se combinan para proporcionar un entorno completo para el desarrollo de DSLs internos en el seno de Scala.

Primeros pasos

La notación infija sienta las base sobre la que construir nuestros lenguajes embebidos en Scala.

Partiendo, por ejemplo, del siguiente trait:

trait MovingRobot {
  def moveForward(): MovingRobot = {
    println("Robot moved one position forward")
    this
  }
  def moveBackward(): MovingRobot = {
    println("Robot moved one position backward")
    this
  }
}

Que puede mezclarse en la declaración de un objeto:

object robot extends MovingRobot

Podemos modelar los movimientos del un robot virtual llamando a los métodos de MovigRobot de una forma tradicional:

robot.moveForward.moveBackward

Pero el uso de la notación infija proporciona da lugar a un código mucho más cercano al lenguaje natural:

robot moveForward() moveBackward
robot moveForward
robot moveBackward

Este es el tipo m√°s simple de los DSLs embebidos en Scala y sirve como punta de partida para enfoques m√°s avanzados.

Transiciones de estado

Sí, simple y también imperativo además de ser de poca utilidad. Los comandos no están cambiando el estado del sistema más allá del efecto lateral que implica la impresión de caracteres por la salida estándar realizada por medio de println:

simpliest_automaton

Con DSLs de este nivel, existen dos opciones para modelar los efectos de las instrucciones del DSL:

  • El enfoque mutable:¬†Es el m√°s sencillo para aquellos llegados a Scala desde lenguajes imperativos pero, definitivamente, es mucho m√°s arriesgado en lo que a la introducci√≥n de bugs respecta. La idea es muy similar a a que hay detr√°s de tantos builders o acumuladores de Java, por ejemplo,¬†StringBuilder:El estado del acumulador es la cadena que se est√° componiendo. M√©todos, como¬†append(double¬†d),¬†devuelven una referencia a la instancia del acumulador cuyo estado se est√° modificando como consecuencia de la llamada. De esta manera, la misma referencia se devuelve llamada tras llamada ya que es la misma instancia de¬†StringBuilder¬†la que muta. ¬ŅSuena familiar?

    mutable_state

  • El enfoque inmutable (o el camino a la sabidur√≠a):¬† ¬°No se debe cambiar nada! Hay que devolver un nuevo estado con los atributos derivados del estado anterior y la acci√≥n realizada. De ahora en adelante, s√≥lo se tratar√° esta t√©cnica.

La belleza de la segunda soluci√≥n radica en que cada acci√≥n devuelve un √ļnico estado que mantiene una relaci√≥n 1 a 1 con los estados reales del sistema modelado. Esto significa que las entidades del c√≥digo que implementa al DSL son un reflejo exacto de los cambios y estados que se desean representar. Adem√°s, el estado es inmutable por definici√≥n.

estado
Del lat. status.Escr. con may. inicial en aceps. 6 y 7.
1. m. Situación en que se encuentra alguien o algo, y en especial cada uno de sus sucesivos modos de ser o estar.

(www.rae.es)

Explicar el por qué del hecho de que la programación basada en la inmutabilidad es más segura (en lo que la introducción de bugs respecta) que la basada en la mutabilidad de objetos está fuera del alcance de este artículo, cientos de explicaciones están al alcance de buscador. Algunas razones están muy bien resumidas en este artículo de IBM.

Incluso, los creadores de Java, decidieron que la inmutabilidad era mejor, al menos para sus cadenas de texto.

state_transition

Devolviendo en cada transici√≥n un, totalmente nuevo, estado se reduce la lista de responsabilidades del c√≥digo que implementa dicha transici√≥n a s√≥lo una: Generar un nuevo estado. Esto genera dise√Īos mucho m√°s sencillos para el conjunto del DSL. No pueden darse¬†cambios inesperados fuera de llamadas expl√≠citas a los m√©todos de transici√≥n.

Al grano de las  transiciones inmutables

Siguiendo el extremadamente complejo ejemplo de nuestra API para robots unidimensionales (llegados a este punto, un ávido lector de Scalera probablemente se de cuenta de que el anterior reto Scalera incluía un bonito DSL). Este API puede cambiarse para seguir el enfoque funcional arriba descrito:

// All states extend `RobotState`
trait RobotState {
  def position: Int
}

// Transitions which can be mixed with any state for which they
// make sense.

trait MovementTransitions {
  self: RobotState =>

  def moveForward(nSteps: Int = 1): RobotState with MovementTransitions

  def moveBackward(nSteps: Int = 1): RobotState with MovementTransitions

}

// States
// In this example, states only differ in the robot position so they all
// are represented by the same case class.
case class Robot(position: Int) extends RobotState with MovementTransitions {

  def moveForward(nSteps: Int = 1) =
    Robot(position + nSteps)

  def moveBackward(nSteps: Int = 1) =
    Robot(position - nSteps)

}

// Initial state
val robot = new Robot(0)

A continuación, un sencillo ejemplo de uso:

robot moveForward(10) moveBackward() position

Esto es una simplificación extrema que muestra las técnicas básicas detrás de los DSLs embebidos en Scala que puede resumirse en:

  • El uso de la notaci√≥n infija.
  • De familias de estados y transiciones entre estos.
  • Limitaci√≥n de las transiciones de forma que s√≥lo pueden producirse desde un estado y resultar en otro completamente nuevo que, podr√≠a compartir, la mayor√≠a de los atributos con su predecesor.

Un poco de teor√≠a:¬†M√°quinas de estado ¬ŅEn serio?

¬ŅEl realmente necesario un modelo de dise√Īo y programaci√≥n basado en m√°quinas de estado? ¬†La respuesta es s√≠, siempre que no quieras acabar d√°ndote¬†un tiro en tu propio pie.

shootfootLas m√°quinas de estados inmutables son sencillas de mantener, entender y expandir.

Por otro lado, hay que tener en cuenta que los DSLs no son otra cosa que lenguajes formales con gramáticas que tienen su lugar en la clasificación de Chomsky: Habitualmente Gramáticas regulares y gramáticas independientes de contexto.

  • ¬ŅQu√© tipo de m√°quina es capaz de procesar/reconocer lenguajes basados en gram√°ticas regulares? Los aut√≥matas finitos.
  • En el caso de las gram√°ticas independientes de contexto, son los aut√≥matas de pila los que pueden reconocer/procesar sus lenguajes. Estos (¬°Ojo! Simplificaci√≥n) no son m√°s que aut√≥matas finitos que pueden hacer uso de una pila auxiliar d√≥nde colocar y de d√≥nde leer s√≠mbolos que pueden determinar el resultado de una transici√≥n, junto con el estado anterior y la entrada.

El modelo de transiciones expuesto en las secciones anteriores parece encajar a la perfecci√≥n con las m√°quinas te√≥ricas de estado. Surge una pregunta que es, as su vez, su propia respuesta: ¬ŅDeber√≠a un desarrollador invertir su tiempo en buscar nuevas soluciones, poco probadas y que pueden ser inestables o estar cargadas de bugs cuando tiene¬†un modelo tan firme a su alcance?

РBien, les sugiero caballeros que inventen una forma de encajar una clavija cuadrada en un agujero redondo.

gene_krantz

–¬†Pero, se√Īor, ¬°Si nos sobran clavijas redondas!

engineer

En el siguiente episodio…

Desarrollo pr√°ctico de un DSL √ļtil, paso a paso. Sin historias de guerra, teor√≠a y sin ninguna de esas cosas “tan aburridas”. S√≥lo…

letsdoit

Scalera tips: How NOT to change your actor’s state

A common key when working with Akka, is modifying properly our actor’s state. If we jog our memory, this framework paradigm allows us to program modeling the concurrency based on actors and message passing. From that base, we could define an actor as a computing unit that may have state and perform tasks based on messages that it will receive through its mailbox and that will be processed sequentially .

That means that, in order to avoid side effects, the actor’s state modification has to take place when processing a message. So senseful so far. However, it’s a pretty common mistake to do something similar to this:

class MyActor extends Actor {

  var state: Int = 0

  def receive = {

    case "command" => 
      Future(state = 1)

    case "someOtherCommand" => 
      state = 2

  }

}

In this case, we have no more warranty that the state change (whose only responsible of keeping it consistent and thread safe is the actor) might cause side efects given that, in the precise moment where the Future modifies the var, it’s possible that the state is being modified by the actor itself (probably as a reaction to some other received message).

This Future[Unit] might not be a block like that. It could be the result of having asked to some other actor:

class MyActor extends Actor {

  type State = Int

  var state: State = 0

  def receive = {

    case "command" => 
      (service ? "giveMeMyNewState").map{
        case newState: State => state = newState
      }

    case "someOtherCommand" => 
      state = 2
  }

}

Something that probably none of us has ever tried.

giphy

The proper way

If we want to modify the actor’s state as result of having previously asked to some other actor and without breaking the concurrency control of the actor, it could be achieved like this:

class MyActor extends Actor {

  type State = Int

  var state: State = 0

  def receive = {

    case "command" => 
      (service ? "giveMeMyNewState") pipeTo self

    case "someOtherCommand" => 
      state = 2

    case newState: State => 
      state = newState
  }

}

With pipeTo we specify to send to certain actor the result of having evaluated some future when its resolved. This way we’re indicating that, when we get the response of the other actor, it will be sent to our mailbox, so it will be processed like a normal message, sequentially.

bill_murray_gif_1

Easy peasy ūüôā

Scalera tips: Como NO modificar el estado de tu actor

Una cuestión habitual a la hora de trabajar con Akka, es modificar de manera correcta el estado de nuestro actor. Si recordamos la base del paradigma de este framework que nos permite programar modelando la concurrencia en base a actores y el paso de mensajes, es que un actor puede definirse como una unidad computacional que puede tener estado y realiza tareas en base a mensajes que recibe en su mailbox y que procesará de manera secuencial.

Esto significa que, para no tener efectos de lado, es necesario que la modificaci√≥n del estado del actor se haga al procesar un mensaje. Hasta aqu√≠ todo tiene sentido. No obstante, es un fallo bastante com√ļn el hacer algo del siguiente estilo:

class MyActor extends Actor {

  var state: Int = 0

  def receive = {

    case "command" => 
      Future(state = 1)

    case "someOtherCommand" => 
      state = 2

  }

}

En ese caso, ya no tenemos garant√≠a de que el cambio de estado (cuyo √ļnico responsable de mantenerlo consistente y thread-safe es el actor) puede generar efectos de lado dado que en el momento en que futuro modifica el var es posible que el estado est√© siendo modificado por el propio actor, desencadenado por el procesamiento de otro mensaje.

Este Future[Unit] puede no ser un bloque como tal, sino el resultado de haber preguntado a otro actor:

class MyActor extends Actor {

  type State = Int

  var state: State = 0

  def receive = {

    case "command" => 
      (service ? "giveMeMyNewState").map{
        case newState: State => state = newState
      }

    case "someOtherCommand" => 
      state = 2
  }

}

Algo que probablemente nadie de nosotros haya intentado jam√°s.

giphy

La forma correcta

En caso de querer modificar el estado del actor como resultado de dicha consulta a otro actor sin romper el control de concurrencia sobre el estado, se podría hacer como sigue:

class MyActor extends Actor {

  type State = Int

  var state: State = 0

  def receive = {

    case "command" => 
      (service ? "giveMeMyNewState") pipeTo self

    case "someOtherCommand" => 
      state = 2

    case newState: State => 
      state = newState
  }

}

Con pipeTo lo que hacemos es mandar a cierto actor el resultado de evaluar un futuro cuando este se resuelva. De esta manera estamos indicando que, cuando tengamos la respuesta del otro actor, se envie a nuestro mailbox, de manera que se procesar√° como otro mensaje m√°s, de manera secuencial.

bill_murray_gif_1

Easy peasy ūüôā

Scalera tips : var immutable vs. val mutable

Let’s suppose we want to define a class that has state (a list, e.g.).
If we followed the pure functional paradigm, each time we changed our instance’s state, this should return a new modified instance (with the new state).

class Foo(val state: List[Int]) {

  def mutate(n: Int): Foo =
    new Foo(state :+ n)

}

val initialFoo = new Foo(List())
assert(initialFoo.state==List())

val mutatedFoo = initialFoo.mutate(1)
assert(initialFoo.state==List())
assert(mutatedFoo.state==List(1))

…if you just think about it, it’s a case class copy method behavior.

But let’s handle the situation where we have to integrate with some legacy application and we cannot manage changes over our Foo generating a new instance.

In this case, the state is determined by the internal list, and not the Foo instance itself.

A first approach could be using a val with a mutable list(which is thread-safe):

import scala.collection.mutable
class Foo {

  private val _state: mutable.ListBuffer[Int] = 
    ListBuffer()

  def mutate(n: Int): Unit = 
    _state += n

  def state: mutable.ListBuffer[Int] =
    _state

}

austinpowers

And, on the other hand, we could use a var with an immutable list (it’s not thread-safe):

class Foo {

  private var _state: List[Int] = 
    List()

  def mutate(n: Int): Unit = 
    synchronized(_state :+= n)

  def state: List[Int] = 
    synchronized(_state)

}

Note that we’ve had to use a synchronize for protecting the state facing concurrent accesses.

Which is the best option?

Even it’s true that the second option generates more boilerplate, using the first one we could have the following situation:

val foo = new Foo
assert(foo.state==mutable.ListBuffer())
val retrievedState = foo.state += 2
assert(foo.state==ListBuffer()) //ERROR!

When returning the mutable internal list, we’ve broken the referential transparency principle and we’ve lost the access control over the state.

It’s probably dependent on the use case, but in general, the functional approach aims to generate new modified copies so potential side effects are avoided.