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

Anuncios

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

Spark Streaming + Twitter : Analytics to one’s taste

Some days you feel handyman, some others you don’t. Today is one of those first.
In this post we’ll create a small application with Spark Streaming that allows us to perform analytics over Twitter in a very basic way. Filtering is just the beginning: the limit is your imagination 🙂

giphy (1)

Step 1: Create your OAuth token at Twitter

Before start coding like crazy, we have to get an OAuth token for being able to use Twitter’s API. It’s really easy and you can follow the steps as described in Twitter’s official documentation.

Once created, if you log in at apps.twitter.com and you click on the application you have just created, inside Keys and Access Tokens tab you’ll get all the data that our application requires:

  • Consumer Key (API Key)
  • Consumer Secret (API Secret)
  • Access Token
  • Access Token Secret

Step 2: Defining our DSL

For that purpose, you can fork or just clone the twitter-stream project that can be found at Scalera’s Github account. In this section, we’ll cover in a flash all components that take part in the project.

The core: Source

The core of this PoC is deploying a Spark Streaming application that reads from some source (in this case Twitter). We have to mention that Spark’s streaming is NRT (Near Real Time) given that, based on certain N window size, every N seconds a new batch will be created with all elements that were written in the source in that time window.

That data stream in Spark is called DStream, while each of those batches is called RDD. For people that is not familiarized with Spark, an RDD represents a collection that is distributed among the Spark cluster (Resilient Distributed Dataset).

If we take a look at Source trait (Later on we’ll see which functionality both Actions and Filters provide):

trait Source extends Actions with Filters {
  _: util.Config =>

  type Content = twitter4j.Status

  val conf = new SparkConf()
    .setAppName(config.getString(Config.AppName))
    .setMaster(config.getString(Config.SparkMaster))

  val windowTime = Seconds(config.getInt(Config.WindowSeconds))

  val ssc = new StreamingContext(conf, windowTime)

  lazy val stream = TwitterUtils.createStream(ssc, None, filters)

}

as you’ll see, we define some Spark configuration (SparkConf) from the parameters defined in the configuration file.

We also define the time window size and instantiate a StreamingContext that we will use to contain our next-declared stream. TwitterUtils (it’s imported) provides the way to instantiate the stream, which requires the Streaming context and the key words we want to use for filtering.

It’s good to know that, by creating the stream, it won’t start listening new items. It will be evaluated lazily. This is explained by the need of defining in an early moment all those actions that we will want to apply to each new batch in our stream.

Defining filters

No gimmicks: just a private synchronized variable that allows adding filters as a String sequence. When creating the stream, as we’ve seen when talking about Source, these filters are passed as parameters.

trait Filters {

  private var _filters: Seq[String] = Seq()

  def filters = synchronized(_filters)

  def filter(filters: String*): Unit =
    synchronized(_filters = _filters ++ filters.toSeq)

}

How to define stream’s behavior: Actions

The Actions trait is in charge of adding actions/handlers/callbacks to execute each time a new batch is received. These actions will be saved inside actions variable. For adding a new action (Action is just a type alias for a batch – RDD[Content] – to Unit function), method when is invoked.

Once we have defined all possible actions to perform, we’ll start receiving items in the stream after having invoked listen method. It will settle that all defined function inside actions will be applied over the stream and then it starts listening.

trait Actions {
  _: util.Config =>

  type Content

  type Action = RDD[Content] => Unit

  private var actions: List[Action] = List()

  val ssc: StreamingContext

  val stream: ReceiverInputDStream[Content]

  def listen(): Unit = {
    actions.foreach(f => 
      stream.foreachRDD(rdd => f(rdd)))
    ssc.start()
  }

  def when(action: Action): Unit = {
    actions = actions :+ action
  }

}

…the rest of it: Credentials and Analytics

Credentials has the purpose of reading from the configuration file all parameters relative to the security token and write them as JVM properties.

The Analytics trait extends from all previously defined components, so using it will be as easy as

object Boot extends Analytics

Example

In first place, we’re going to modify the configuration file for using the token we have just created: we replace the ‘secret’ word in src/main/resources/app.conf with real values.

Once this is done, we’ll add all the filters we want (for dealing only with the tweets than contain certain key workds):

  filter(
    "dance",
    "music"
  )

We’ll later indicate with one (or several) when clauses the actions to perform when a new bunch of tweets is arrived. For example, we count and print them out:

when { tweets =>
  logger.info(s"Received tweets [${tweets.count()}}]")
  tweets.foreach {
    tweet => logger.info(s"\n$tweet")
  }
}

For making our exaple work, you only have to execute (over the project dir):

sbt run

And tweets should start appearing among your messy blog output!

idwzlyrarjaylstrg8q7

Easy peasy 🙂

Spark Streaming + Twitter : Analytics al gusto

Hay días en que te sientes manitas y días en los que no. Hoy es de los primeros.
En este post vamos a crear una pequeña aplicación con Spark Streaming que nos permita realizar analytics de manera muy básica sobre Twitter. Filtrar es solo el principio: el límite lo que marque tu imaginación 🙂

giphy (1)

Paso 1: Crear token de OAuth en Twitter

Antes de ponernos a codificar como locos, debemos hacernos con un token de OAuth para poder usar la API de Twitter. Es realmente sencillo y podeis seguir los pasos tal y como se describe en la documentación oficial de Twitter.

Una vez creado, si os logais en apps.twitter.com y pulsáis sobre la aplicación que habéis creado, en la pestaña Keys and Access Tokens podréis obtener los datos que nos van a hacer falta en nuestra aplicación:

  • Consumer Key (API Key)
  • Consumer Secret (API Secret)
  • Access Token
  • Access Token Secret

Paso 2: Definiendo nuestro DSL

Para ello haced fork o simplemente descargaros el proyecto twitter-stream que se encuentra en el Github de Scalera. En esta sección daremos un repaso rápido a los componentes que forman el proyecto.

El core: Source

El core de esta prueba de concepto es desplegar una aplicación Spark Streaming que lee de una determinada fuente (en este caso de Twitter). Cabe destacar que el streaming de Spark es NRT (Near Real Time) ya que, en base a un tamaño de ventana de tiempo N, se creará un batch cada N segundos con todos los elementos que hayan entrado en dicha ventana.

Al stream de datos, en Spark, se le denomina DStream, mientras que cada uno de esos batches se llama RDD, que para la gente que no esté familiarizada con Spark core, representan una colección distribuida en el cluster de Spark (Resilient Distributed Dataset).

Si echamos un vistazo al trait Source (Más adelante veremos qué funcionalidad aportan Actions y Filters):

trait Source extends Actions with Filters {
  _: util.Config =>

  type Content = twitter4j.Status

  val conf = new SparkConf()
    .setAppName(config.getString(Config.AppName))
    .setMaster(config.getString(Config.SparkMaster))

  val windowTime = Seconds(config.getInt(Config.WindowSeconds))

  val ssc = new StreamingContext(conf, windowTime)

  lazy val stream = TwitterUtils.createStream(ssc, None, filters)

}

vemos que definimos una configuración de Spark (SparkConf) a partir de los parámetros definidos en el fichero de configuración.

También definimos el tamaño de ventana e instanciamos un StreamingContext sobre el que declarar nuestro stream stream. La forma de instanciarlo la aporta TwitterUtils (que importamos), y necesita el Streaming context y las palabras claves sobre las que filtraremos los tweets.

Es importante mencionar que, no por crear el stream, este se pone a escuchar, sino que se evaluará de manera perezosa. Esto es así para poder definir las acciones que queremos aplicar con cada nuevo batch en nuestro stream.

Definiendo filtros

Sin trampa ni cartón: solo una variable privada con un synchronized que permite añadir filtros como Strings a una secuencia. A la hora de crear el stream, como hemos visto en Source se pasan como argumento dichos filtros.

trait Filters {

  private var _filters: Seq[String] = Seq()

  def filters = synchronized(_filters)

  def filter(filters: String*): Unit =
    synchronized(_filters = _filters ++ filters.toSeq)

}

Cómo definir el comportamiento del stream: Actions

El trait Actions se encarga de añadir acciones / handlers/ callbacks a ejecutar cada vez que se reciba un nuevo batch. Estas acciones se guardan en actions. Para añadir una nueva acción (Action no es más que un type alias para una función de batch – RDD[Content] – a Unit), se invoca al método when.

Una vez que hayamos definido todas las acciones a realizar, comenzaremos a recibir contenido en el stream invocando a listen. Este aplica las funciones definidas en actions sobre el stream y luego comienza a escuchar del mismo.

trait Actions {
  _: util.Config =>

  type Content

  type Action = RDD[Content] => Unit

  private var actions: List[Action] = List()

  val ssc: StreamingContext

  val stream: ReceiverInputDStream[Content]

  def listen(): Unit = {
    actions.foreach(f => 
      stream.foreachRDD(rdd => f(rdd)))
    ssc.start()
  }

  def when(action: Action): Unit = {
    actions = actions :+ action
  }

}

…y el resto: Credentials y Analytics

Credentials se encarga de leer del fichero de configuración los parámetros relativos al token de seguridad y escribe dichas propiedades como propiedades en la JVM.

El trait Analytics, extiende de todos los componentes antes definidos, de manera que para usarlo sea tan sencillo como hacer

object Boot extends Analytics

Ejemplo

En primer lugar vamos a modificar el fichero de configuración para que utilice el token que acabamos de generar: sustituimos el comodín secret en src/main/resources/app.conf por los valores reales.

Una vez hecho esto, añadimos los filtros que queramos (para trabajar solo con los tweets que contengan ciertas palabras clave):

  filter(
    "dance",
    "music"
  )

Y posteriormente indicamos con una (o varias) sentencias when la acción a realizar cuando llegue un nuevo grupo de tweets. Por ejemplo, los contamos e imprimimos por pantalla:

when { tweets =>
  logger.info(s"Received tweets [${tweets.count()}}]")
  tweets.foreach {
    tweet => logger.info(s"\n$tweet")
  }
}

Para hacer funcionar nuestro ejemplo bastará con ejecutar (sobre el directorio del proyecto):

sbt run

¡Y los tweets deberían ir apareciendo en tu abultada salida de log!

idwzlyrarjaylstrg8q7

Easy peasy 🙂

Scalera challenge: From Earth to the Moon

We have started a new year, and we want to make some solid headway. As we promised, one of our New Year’s resolutions is trying to innovate and make this blog more social. For this reason, let us introduce you the first Scalera challenge, just for driving you crazy 🙂

From Earth to the Moon

The ESA (European Space Agency) wants to send two robots (rovers) to the Moon. They are fitted with a sophisticated landing support: a parachute. The problem here is that it seems both robots won’t land at the same place…
Once they’ve landed at the same place as their own parachutes at the Moon, which has a discrete surface of infinite size, both rovers must find each other to start the mission together.

The challenge consists on programming the rovers using a limited command set, so they finally manage to find each other. Keep in mind that the same program will be loaded on both robots and they don’t have any programatic way to know which their identifiers are.

DSL for manouver

The command set that will be used for programming the rovers is composed of:

  • MOVE <direction> : It moves the rover a discrete space unit to the left or to the right.
  • IF FOUND PARACHUTE <command> : If the rover finds a parachute on the floor, at its feet wheels, then execute the given command.
  • NOP: Rover does nothing…
  • GOTO <lineId> : It jumps to the command placed at the given line.

Participate!

moonrover_example

You can find the project for start coding your robots at github.
You’ll find all the technical info you might need at the README.md file.

If you want to participate, just drop us an email to scalerablog at gmail dot com with the Launch.scala file that contains the program you have designed to solve the problem. Don’t forget to include your twitter user name for mentioning you at our virtual hall of fame 😛

The deadline will be two natural weeks from this post publication.

Among all best solutions (less clock ticks, less program lines,…) we’ll raffle the blog t-shirt 🙂

Come on and good luck!

How many things have been denied one day, only to become realities the next!

Jules Verne

Scalera challenge: De la Tierra a la Luna

Hemos entrado en el nuevo año, y hemos querido hacerlo pisando fuerte. Como prometimos, uno de nuestros propósitos para este año es tratar de innovar y hacer más participativo el blog. Por ello, os presentamos nuestro primer desafío Scalera, para poner a prueba vuestras cabezitas 🙂

De la Tierra a la Luna

forever-alone-moon_o_1332879

La ESA (Agencia Espacial Europea) quiere mandar dos robots (rovers) a la Luna. Para aterrizar disponen de un sofisticado sistema: un paracaídas. El problema es que todo apunta a que los robots no aterrizaran en el mismo sitio…
Una vez aterrizados, justo encima de sus propios paracaídas, en la Luna, la cual tiene una superficie discreta de tamaño infinito, ambos rovers deben encontrarse para empezar el desarrollo de la misión juntos.

El reto consiste en programar, utilizando un conjunto de instrucciones limitado, el rover de manera que se acaben encontrando. Ten en cuenta, que el mismo programa será cargado en ambos robots y los rovers no tienen forma programática de saber su identificador.

DSL de navegación

El set de instrucciones para programar los rovers se compone de:

  • MOVE <direction> : Mueve el rover una unidad del espacio discreto hacia la izquierda o la derecha.
  • IF FOUND PARACHUTE <command> : Si el rover encuentra un paracaídas en la parcela de suelo donde se encuentra, entonces ejecuta la instrucción indicada.
  • NOP: El rover no hace nada…
  • GOTO <lineId> : Salta a la instrucción que se encuentra en la línea indicada.

¡Participa!

moonrover_example

Puedes encontrar el proyecto para empezar a programar tus robot en github.
Encontrarás toda la información técnica necesaria en el fichero README.md.

Para participar solo teneis que mandarnos a scalerablog at gmail dot com el fichero Launch.scala con el programa que habéis usado y vuestro usuario de twitter para mencionaros en nuestro hall de la fama virtual 😛

El plazo de participación finaliza en dos semanas naturales desde la fecha de publicación de este post.

Entre las mejores soluciones (menos ticks de reloj empleados, menos lineas de programa) sortearemos una camiseta del blog 🙂

¡Ánimo y mucha suerte!

Cuántas cosas negadas la víspera han sido una realidad al día siguiente

Julio Verne