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 : 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.

Scalera tips : var inmutable vs. val mutable

Supongamos que queremos definir una clase que tiene estado (una lista, por ejemplo).
Si siguiéramos el paradigma funcional puro, cada vez que modificáramos el estado de nuestra instancia, esta debería devolver una instancia nueva modificada:

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

…si te paras a pensarlo, es el comportamiento del método copy de una case class.

Pero pongámonos en el caso en que debemos integrarnos con una aplicación legacy y no podemos gestionar los cambios sobre nuestro Foo generando una nueva instancia.

En ese caso, el estado lo determina la lista interna, y no la instancia misma de Foo.

Una primera aproximación podría ser utilizar un val de tipo lista mutable (es threadsafe):

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

Y en la otra mano tendríamos la opción de usar un var inmutable (no es thread-safe):

class Foo {

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

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

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

}

Fijaros que hemos tenido que hacer un synchronize para proteger el estado frente a accesos concurrentes.

¿Qué es mejor?

Si bien es cierto que la segunda opción genera mayor boilerplate, ocurre que con la primera podría darse el siguiente caso:

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

Al devolver la lista, hemos roto el principio de transparencia referencial y perdemos el control de acceso sobre la lista.

Es probable que dependa del caso de uso de cada uno, pero en general, el enfoque funcional anima a generar nuevas copias de manera que se evitan potenciales efectos de lado.