Aprendiendo AWS: Lambda usando VSCode y Python

Continuando con la serie sobre AWS, en este caso he decidido jugar un poco con una de las plataformas del momento, Lambda. Para ello he construido una API con Python para leer y escribir datos de una base de datos, en local y con diferentes capas de test.

¿Qué es Lambda?

Lambda es un producto de AWS que permite ejecutar trozos de código (funciones) de manera aislada de otras aplicaciones. Esto aporta una capa extra de abstracción con todo lo bueno y malo que ello implica, y además únicamente se factura por el tiempo en el que la función se está ejecutando.

Se puede interactuar con Lambda utilizando el editor en la nube que ofrecen, subir el código a S3 usando un formulario, o utilizar sistemas como SAM local para automatizar todo el proceso, y este último es el que se ha usado en este artículo.

El objetivo

Para este pequeño proyecto, el plan consistía en crear al menos un par de funciones que expusieran una API REST, escribir en una base de datos, definir todo lo posible mediante código y configuración y que el despliegue se realizara utilizando la consola.

El ejemplo es un banco con cinco funciones, crear, depositar, retirar, ver saldo, y hacer una transferencia.

Escribiendo la primera función

El lenguaje utilizado en este caso ha sido Python, así que parte del reto ha sido aprender a escribir utilizando la sintaxis del lenguaje de la serpiente:

def setup(event, context):
    payload = json.loads(event['body’])
    account = payload['id']
    bank = Bank()
    bank.save_account({"id": account, "balance": 0})
    return {
        "statusCode": 201
    }

En Lambda, y para el caso particular de Python, se define una función como un método que acepta dos parámetros, event que contiene los datos que se le pasan a la función, y context que contiene información relativa a la ejecución de la misma.

En este ejemplo se puede ver los valores que se pasan a esta función dentro de event

{'httpMethod': 'PUT', 'body': '{"id": "test_alice"}', 'resource': '/account', 'requestContext': {'resourcePath': '/account', 'httpMethod': 'PUT', 'stage': 'prod', 'identity': {'sourceIp': '127.0.0.1:61479'}}, 'queryStringParameters': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Content-Length': '20', 'Host': 'localhost', 'User-Agent': 'python-requests/2.18.4', 'X-Forwarded-Port': '', 'X-Forwarded-Proto': ''}, 'pathParameters': None, 'stageVariables': None, 'path': '/account', 'isBase64Encoded': False}

Para poder continuar, es necesario interpretar los parámetros. En este caso, el valor body es un JSON con la información de la cuenta, y para ello se crea un objeto a partir de la cadena de texto utilizando json.loads(event['body’])

Esto permite interactuar con los datos que se pasan a la función, que en este caso lo que hace es guardar la nueva cuenta en la clase Bank creada para abstraer toda la información relativa a la base de datos.

Finalmente, la función ha de devolver un objeto que contenga, al menos un campo con el estado HTTP en statusCode, que en este caso es 201 (Created).

Utilizando la misma lógica se puede hacer las operaciones de depósito, retirada, saldo y transferencia, aunque para ello puede resultar interesante abstraer la función que recibe el evento de la lógica de la función, usando una función que se llamará dispatch:

def dispatch(event, context):
    requestContext = event["requestContext"]
    resourcePath = requestContext["resourcePath"]
    method = requestContext["httpMethod"]

    if(method == "GET"):
        payload = event['queryStringParameters']
    else:
        payload = json.loads(event['body'])
    try:

        if(resourcePath == "/account"):
            if(method == "PUT"):
                setup(payload)
                return {
                    "statusCode": 201
                }

            elif(method == "GET"):
                return {
                    "statusCode": 200,
                    "body": get_balance(payload)
                }
        ...

    except Exception:
        return {
            "statusCode": 500
        }

Esta función permite leer los parámetros, interpretar la ruta y llamar a la función específica de la misma, así como hacer un pequeño tratamiento de errores.

Integrando con DynamoDB

Una vez que están todas las funciones definidas, el siguiente paso es escribir la clase Bank que será la que escriba información en la base de datos.
Mantener estas responsabilidades separadas permite, en primer lugar, una mejor capacidad de pruebas, y en segundo lugar, hace al código de la función independiente del proveedor de bases de datos.

import boto3
import os
import uuid

class Bank:

    def __init__(self):
        endpoint = os.environ.get("ENDPOINT")
        if(endpoint is None):
            self.dynamodb = boto3.resource('dynamodb')
        else:
            self.dynamodb = boto3.resource('dynamodb', endpoint_url=endpoint)

        self.accounts = self.dynamodb.Table("Accounts")
        self.transactions = self.dynamodb.Table("Transactions")

    def get_account(self, account_name):
        try:
            result = self.accounts.get_item(Key={'id': account_name})
            return result['Item']
        except:
            return None

    def log_transaction(self, transaction):
        transaction["id"] = str(uuid.uuid1())
        self.transactions.put_item(Item=transaction)

    def save_account(self, account):
        self.accounts.put_item(Item=account)

El único detalle destacable de esta clase, es que comprueba si existe un endpoint para DynamoDB definido, esto permitirá utilizar la misma clase para los tests.

Probando la función

Una vez escrito el código de las funciones y la conexión con la base de datos, el siguiente paso es probar que todo funciona como se espera.

Configurando la base de datos

Para probar la interacción con la base de datos se puede, o bien configurarla utilizando el sdk de aws y el comando aws configure o bien utilizando una base de datos local, que podemos arrancar utilizando el siguiente comando:

java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar --inMemory

Esto proporciona una base de datos de DynamoDB en local lista para utilizar. La creación de tablas, en este ejemplo, se realiza como parte de los test unitarios:

table = dynamodb.create_table(
    TableName="Accounts",
    KeySchema=[
        {
            'AttributeName': 'id',
            'KeyType': 'HASH'
        }],
    AttributeDefinitions=[
        {
                'AttributeName': 'id',
                'AttributeType': 'S'
        }],
    ProvisionedThroughput={
        'ReadCapacityUnits': 5,
        'WriteCapacityUnits': 5
})
            table.meta.client.get_waiter('table_exists').wait(TableName='Accounts')

Probando las funciones

Con la base de datos funcionando y las tablas creadas, llega el momento de escribir las diferentes pruebas, como esta, que comprueba que un ingreso ha sido efectivo:

...

def test_deposit(self):
    function.setup({'id': "Bob"})
    function.deposit({'id': "Bob", "amount": 10.0})
    self.assertEqual(function.get_balance({'id': "Bob"}), 10)

Para ejecutar los tests es tan sencillo como escribir python function-tests.pydesde la consola de comandos.

Configurando Lambda

Para convertir el código de las funciones en una API que se pueda consultar vía HTTP se ha recurrido a SAM (Serverless Application Model) local, un conjunto de herramientas para definir, probar y desplegar funciones Lambda.

La plantilla

Una plantilla de SAM no es más que un subconjunto de una plantilla de CloudFormation, que en un artículo anterior comentaba que es una manera de automatizar la creación y actualización de recursos dentro de AWS.

Para este ejemplo la plantilla tiene el siguiente aspecto:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: 'A simple banking app built on lambdas'

Resources:
  Bank:
    Type: AWS::Serverless::Function
    Properties:
      Handler: function.dispatch
      CodeUri: .
      Runtime: python3.6
      Events:
        CreateAccount:
          Type: Api
          Properties:
            Path: '/account/new'
            Method: put
        GetBalance:
          Type: Api
          Properties:
            Path: '/account'
            Method: get
        Deposit:
          Type: Api
          Properties:
            Path: '/account/deposit'
            Method: post
        Transfer:
          Type: Api
          Properties:
            Path: '/account/transfer'
            Method: post
        Withdraw:
          Type: Api
          Properties:
            Path: '/account/withdraw'
            Method: post
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
              Resource: 'arn:aws:dynamodb:*:*:*'

En esta plantilla se define:
– El handler, o función que responderá a la llamada
– Los eventos que dispararán la llamada (varios eventos pueden disparar el mismo handler)
– Las políticas que se aplican (en esta caso necesitamos permisos de lectura y escritura para la base de datos).

Validando y probando la plantilla con SAM

Una vez definidos todos los valores para la plantilla, se puede ejecutar SAM para comprobar que la plantilla es válida:

sam validate

El cliente SAM permite, además, probar la definición de la plantilla de manera local:
sam local start-api

Este comando inicializa un servidor en el puerto 3000 con el que se pueden probar las funciones desde un cliente HTTP (en este ejemplo he utilizado Postman) y ver como la API responde a las diferentes llamadas.

Pruebas de integración

Para comprobar que todo funciona bien antes de subir la plantilla a AWS, he creado un conjunto de tests un tanto diferentes, esta vez para comprobar que los datos se transforman correctamente, llamando al cliente de localhost.

def test_deposit(self):
    r = requests.get('http://localhost:3000/account?id=test_bob')
    initial_bob_balance = float(r.text)

    r = requests.post('http://localhost:3000/account/deposit', data =  json.dumps({'id':'test_bob', 'amount': 100}))
    self.assertEqual(200, r.status_code)

    r = requests.get('http://localhost:3000/account?id=test_bob')
    new_balance = float(r.text)

    self.assertEqual(new_balance, initial_bob_balance + 100)

Para ejecutar los tests es tan sencillo como escribir python integration-tests.pydesde la consola de comandos.

Desplegando la plantilla

Finalmente queda empaquetar la función y desplegarla en la consola de AWS. En primer lugar se empaqueta la función en un fichero zip, se guarda en una carpeta (bucket) de S3 y se actualizan las referencias en el fichero que se ha denominado package.yaml:

sam package --template-file template.yaml --s3-bucket my-bucket --output-template-file packaged.yaml

En segundo lugar se invoca a CloudFormation para crear todos los recursos definidos en el paquete, incluyendo el código fuente y el conjunto de permisos:

sam deploy --template-file packaged.yaml --stack-name my-stack-name --capabilities CAPABILITY_IAM

Una vez se haya desplegado el paquete, se puede ir a la consola de AWS y ver cómo se ha desplegado la función:

Captura de pantalla 2018-05-12 a las 21.00.03

Al hacer click, se puede ver además los diferentes endpoints que API Gateway ha creado. así como las diferentes opciones:

Captura de pantalla 2018-05-13 a las 9.54.07

Con esto la función creada está lista para ser ejecutada.

En resumen

Eh este artículo se ha definido una función Lambda de manera local, utilizando tantas capas de abstracción como hayan sido necesarias. Se han creado diferentes tipos de test para probar los diferentes puntos de integración, y finalmente se ha empaquetado y desplegado la función desde la línea de comandos.

El código está disponible en GitHub.

Sin embargo, se han quedado algunas cosas fuera del alcance del artículo, y quedan como sugerencia al lector para continuar experimentando:

  • Inyección de dependencias y Mocking para hacer tests unitarios
  • Integración con un sistema como Jenkins para ejecutar tests
  • Crear la base de datos utilizando la plantilla de SAM
  • Uso de frameworks como Serverless que agregan una capa de abstracción incluso mayor, aislando de las implementaciones de AWS, Azure, Google Cloud o IBM.

¿Y tú, has hecho alguna prueba con Lambdas u otra plataforma Serverless?

Herramientas utilizadas

  • SAM local para probar la función en local, así como para desplegarla en AWS.
  • Visual Studio Code para editar el código y para interactuar con la API de SAM local usando su consola integrada.
  • DynamoDB Local para emular una base de datos de DynamoDB.
  • Boto 3 el SDK oficial de AWS para Python.
  • Postman para probar las diferentes APIs tanto locales como remotas.

Más documentación y ejemplos

Otros tipos de inyección de dependencias

No es la primera vez que hablo en este blog sobre inyección de dependencias, el patrón que nos permite pasar todas las dependencias que una clase necesita en el constructor en vez de como argumentos en métodos o utilizar clases estáticas.

En el caso de Java, donde últimamente paso la mayoría de mi tiempo, contamos con Spring y Guice como las maneras más conocidas de inyectar dependencias, veamos un ejemplo de Spring.

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ClassWithDependencies {
    private final DependentClass dependentInstance;

    public void doSomethingWithDependency(){
        dependentInstance.doSomething();
    }
}

@Component
public class DependentClass {
    public void doSomething(){
        System.out.println("Doing something")
    }
}

En este ejemplo, con las anotaciones @Component de Spring y @RequiredArgsConstructor de Lombok (un pre-procesador que nos permite agregar setters, getters y constructores a Java) podemos definir ClassWithDependencies como una clase que recibe DependentClass como parámetro del constructor, y luego operar sobre ella.

En artículos anteriores hemos hablado de otros sistemas como el de ASP.net vNext o incluso crear el nuestro propio. Creando un motor de inyección de dependencias con C#

Es importante destacar que en este ejemplo concreto, la inyección construye una nueva instancia de la clase cada vez que se inyecta, siendo la más sencilla de las opciones de inyección.

Una instancia por ejecución (Singleton)

Sin embargo, podemos personalizar aún más el tipo de inyección que hacemos. Volviendo a nuestro ejemplo anterior, supongamos que DependentClass contiene información de la máquina (memoria, procesador, etc) en la que se ejecuta, y por tanto no cambiará a lo largo de la vida de nuestra aplicación.

En este caso, una nueva instancia de cada clase sería innecesario o incluso costoso, dependiendo del tipo de inyección, para lo cual, en la definición del componente, especificamos qué tipo de inyección queremos

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ClassWithDependencies {
    private final MachineContext machineContext;

    public void doSomethingWithDependency(){
        machineContext.getProcessor();
    }
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MachineContext {
    public String getProcessor() {
        return "Intel";
    }
}

Comparemos esto con definir un patrón singleton de manera manual:

public class ClassWithDependencies {
    private final MachineContext dependentInstance;

    public void doSomethingWithDependency(){
        MachineContext.getInstance().getProcessor();
    }
}

public class MachineContext {
    private static MachineContext instance;

    public String getProcessor() {
        return "Intel";
    }

    private MachineContext() {
    }

    public static MachineContext getInstance(){
        if(instance == null){
            instance = new MachineContext();
        }
        return instance;
    }
}

La principal diferencia de usar Singleton en inyección respecto al singleton estándar que podemos ver en el ejemplo anterior es con inyección de dependencias tenemos las ventajas de singleton y mantenemos la capacidad de probar nuestro sistema, ya que en una prueba de ClassWithDependencies podríamos simular o utilizar un mock de MachineContext (por ejemplo si queremos probar cómo se comporta nuestra aplicación en distintos contextos).

Una instancia por petición (Request)

Una variante de Singleton especialmente para web, ocurre cuando el contexto depende de la petición o la sesión. Para este ejemplo, tenemos una función que se dedica a llevar un registro de las excepciones, y queremos, para cada excepción, registrar además el Id del usuario al que le ocurrió la excepción.

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GlobalExceptionHandler {
    private final Request request;

    public void handleException(Exception ex){
        logger.error(request.getUserId(), ex.getStacktrace())
        machineContext.getProcessor();
    }
}

@Component
@Getter
@Setter
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class Request {
    private String userId;
}

Este formato nos permite rellenar la clase Request en la capa del controlador de de nuestra aplicación, y en la otra directamente preguntar por el estado, sin necesidad de tener que pasar el objeto request a lo largo de toda la cadena de llamadas hasta la captura de esta excepción.

Con ello, evitamos que nuestras clases tengan una referencia al objeto únicamente por tener que pasarlo a la siguiente capa, se simplifica el número de parámetros que manejamos y podemos referirnos al contexto siempre que lo necesitemos.

Conclusiones

En este artículo hemos repasado inyección de dependencias de manera tradicional y además hemos visto las ventajas de utilizar construcciones como Singleton y Request.

Y tú, usas estas construcciones cuando desarrollas aplicaciones?

Scala desde la perspectiva de C# y JavaScript, tercera parte

En los artículos anteriores de esta serie, veíamos una pequeña introducción a Scala, y hacíamos una kata para comprobar que habíamos entendido la sintaxis. En este artículo veremos dos construcciones del lenguaje que resultan bastante interesantes, llamadas traits y case classes.

Traits

Podemos entender los traits como una mezcla entre interfaces y clases abstractas, ya que podemos definir un contrato como haríamos con una interfaz, y por otra parte podemos definir también una implementación.

A diferencia de Java, Scala nos permite implementar varias traits, y podemos utilizarlas para agregar características adicionales a nuestro código. Veamos un ejemplo.

En este caso vamos a definir una clase base llamada Employee, que tiene un salario, y luego vamos a definir varios traits, algunos solamente con valores, y otros con pequeñas comprobaciones.

  • El trait Temporary calcula el mes final del contrato
  • El trait Authority nos permite saber si un empleado tiene gente a su cargo
  • El trait Remote nos permite establecer una localización y una zona horaria
  • Finalmente, el trait InOffice nos permite establecer un despacho.

Con estos cuatro traits podemos componer las clases que forman a nuestros empleados, donde tenemos a Developer, a Manager y a Intern:

Como vemos en el ejemplo, podemos utilizar los traits para definir características adicionales a nuestras clases, y podemos también agregar lógica dentro de las mismas, mezclando los conceptos de interfaz y clase abstracta.

Case classes

La otra cara de la moneda son las Case Classes, que podemos encontrar cuando definimos jerarquías, pero sobre todo las podemos encontrar cuando tenemos un número específico de entidades y lo que realmente cambia es lo que hacemos con ellas. Recuperando el mismo caso de empleados, becarios y managers, vamos a redefinirlo como Case Classes:

En este caso hemos simplificado la manera de definir los diferentes datos, y solamente utilizaremos la fecha inicial para Dev y Manager, y el tiempo total de estancia para Intern. En este caso, podemos asumir que nuestro conjunto de datos está fijo.

Sin embargo, ¿qué pasaría si necesitáramos agregar información de nóminas a nuestra aplicación? Si recurriéramos a la herencia, deberíamos implementar un método “salary” o similar, para cada una de las clases, sin embargo, como estamos usando “case classes” podemos crear un único método que las compruebe todas, y se comporte de manera consistente:

En este caso, el método nos devolverá distintos resultados en función de la clase que estemos aplicando y los datos que contenga la misma, permitiendo una separación entre las clases que contienen los datos de los métodos para manipularlos.

Este enfoque tiene sus inconvenientes, y es que si tenemos que agregar un nuevo tipo de dato (por ejemplo, External) tendremos que cambiar todos los métodos auxiliares para soportar el nuevo caso, así que se reduce a elegir en función de las necesidades de nuestro proyecto.

Conclusiones

Construcciones como Case Classes o Traits nos permiten establecer restricciones de una manera sencilla, las traits nos permiten mezclar conceptos de interfaces y clases, y las case classes nos permiten separar los datos de nuestra clase de las transformaciones que hacemos con los mismos.

En C# tenemos a nuestra disposición los métodos de extensión, con los que podemos lograr un comportamiento similar que con las case classes, y que hemos visto con anterioridad en este artículo “Clases abstractas VS interfaces + métodos de extensión“.

En el próximo artículo de la serie veremos más construcciones y, como siempre, más ejemplos.

Para más información:

Importando el calendario de nuestro evento favorito

El próximo 27 y 28 de noviembre estaré un año más en el Codemotion, un evento que se celebra en Madrid reúne comunidades de todo tipo y del que hemos hablado en otros artículos de años anteriores.

En ellas desarrolladores de .NET, Java y la JVM, Ruby, Python, JavaScript, Objective-C, Swift, PHP y otros, se reunen para enseñarnos lo mejor de todos los mundos. Yo ya tengo mi entrada, si no tienes la tuya ya estás tardando

Sin embargo hay algo que sigo sin entender. Al igual que en otros eventos, tenemos disponible la agenda completa en la web, y probablemente el primer día nos entregarán una copia EN PAPEL que acabará en una papelera varios días después del evento, si no lo perdemos antes.

No sería mucho más conveniente poder tener el calendario oficial del evento en un formato que lo pudiera entender nuestro Google Calendar, Outlook, iCal o cliente que utilicemos, y poder usar nuestro móvil para orientarnos por el evento? Pues bien, eso es lo que he hecho, y es lo que veremos en este artículo:

Primer paso, obtener los datos

Para obtener los datos originales tenemos muchas maneras. Una de ellas es utilizar un servicio como Kimono que nos permite crear una API REST a partir de una página ya existente, lo cual nos puede resultar muy útil para extraer información.

Otra opción consiste en investigar un cómo, y qué datos carga la página. En el caso de Codemotion, la agenda se carga de manera asíncrona, así que tras investigar el panel de red de Chrome podemos ver un fichero en formato JSON que contiene, para nuestro disfrute, la agenda completa a nuestra disposición, así que, misión cumplida.

Segundo paso, comprender el formato de salida

Para poder generar un fichero que sea compatible con los diferentes clientes de email, una de las opciones es utilizar el formato icalendar, que define un evento de la siguiente manera:

BEGIN:VEVENT
SUMMARY:Document like a hero using Asciidoctor
DTSTART:20151127T160000Z
DTEND:20151127T164500Z
DESCRIPTION:In 2013, the Spring team decided to migrate the Starting Guides on spring.io ...
END:VEVENT

Analicemos los diferentes componentes, dejando de lado las cabeceras begin y end del evento:

  • Summary: Título que se mostrará en la cita del calendario
  • Dtstart: Fecha y hora de inicio de la cita en la zona horaria UTC, en el siguiente formato: Año Mes Día (T) Hora Minutos Segundos (Z)
  • Dtend: Fecha y hora de fin de la cita, en el mismo formato anteriormente comentado
  • Description: Detalles del evento

Si hemos utilizado el calendario de manera corporativa echaremos de menos temas como invitados, alarmas, etc, aunque todos ellos forman parte del estándar y podemos utilizarlos sin problemas. Para reunir varios eventos en un fichero iCalendar hemos de establecer unas cabeceras y un pie de página de la siguiente manera:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
...
END:VCALENDAR

En este código, definimos nuestro fichero iCalendar, la versión del mismo, y el identificador del generador (que en este caso lo he tomado directamente del ejemplo de la wikipedia). Cerramos el fichero con el marcador End.

Con esta información, ya podemos generar nuestro calendario.

Tercer paso, generar el calendario

Para este ejemplo, como utilizaba un fichero en formato json, me pareció mucho más sencillo utilizar JavaScript y Node.js para generar el fichero ics.

Una de las cosas que nos aporta JavaScript, es que, al ser dinámicamente tipado, podemos abstraer la consola en un objeto creado por nosotros, y asignarlo o no a la consola en función de nuestra necesidad.

Como detalle a tener en cuenta está el uso de saltos de línea, ya que hemos de utilizar \n, de otra manera, se generará una nueva línea en el fichero de texto resultante y tendremos un error al importarlo.

Además, aunque parezca obvio, podemos tener más de un evento en un mismo fichero ics.

Cuarto paso, probarlo

Por último, y no por ello menos importante, es necesario confirmar que nuestro calendario se ha generado correctamente, para lo que podemos utilizar servicios como iCalendar validator. Finalmente recomiendo utilizar alguna aplicación de escritorio (como iCal en Mac) para hacer un par de importaciones y comprobar que todos los eventos se generan correctamente.

Conclusiones

Podemos generar un calendario utilizando el formato ics y teniendo una fuente de origen de datos. Si nuestra fuente está en formato json, podemos utilizar JavaScript de manera sencilla para movernos por el mismo, y generar nuestro fichero de resultado, que, por cierto, tienes disponible públicamente en Github.

Yo ya tengo mi calendario, y tú?

Enlaces adicionales:

Scala desde la perspectiva de C# y JavaScript, segunda parte

En la primera parte de esta serie vimos una primera introducción a Scala como lenguaje de programación definiendo algunas características de su sintaxis. En esta segunda parte pasaremos a la práctica utilizando una Code Kata.

En las artes marciales, se denomina kata a una representación, individual o colectiva, de un conjunto de movimientos. En disciplinas como el Karate se suelen enseñar en exhibiciones. El objetivo de las mismas es repetir los movimientos una y otra vez hasta que los mismos surjan de manera natural.

En programación, denominamos code kata a una definición de un problema relativamente simple, que resolvemos una y otra vez cuando estamos aprendiendo un lenguaje o una técnica como TDD. En este caso elegiremos una kata llamada Karate Chop, que define lo siguiente:

Dada una lista ordenada y un número, devolver la posición del mismo en la lista o -1 si no se encuentra, utilizando búsqueda binaria para ello.

El objetivo en este caso era ver si podía aprender lo mínimo necesario para poder escribir una solución a esta Kata utilizando Scala y utilizando la sintaxis de programación estructurada o orientada a objetos a la que estoy acostumbrado. Tras varias iteraciones agregando tests y condiciones, llegué al siguiente resultado:

Para hacer las pruebas se suele usar un framework como ScalaTest, o recurrir a JUnit o TestNG de Java, que, como hemos mencionado anteriormente, son compatibles. En mi caso me limité a crear una pequeña función auxiliar que simplemente comparaba el resultado de la función y escribía por pantalla si había discrepancias:

Finalmente, para comprobar que el código “funciona” he usado el siguiente conjunto de casos de prueba:

Escribiendo esta función aprendí varias cosas sobre Scala:

  • No necesitamos escribir el tipo de dato, ya que tenemos las palabras reservadas var para variables y val para objetos inmutables. Scala infiere el tipo de dato en tiempo de compilación por nosotros de la misma manera que lo hace C#, de manera que tenemos un sistema de tipos sólido y más sencillo de escribir.

  • Como curiosidades de sintaxis, el tipo de respuesta de una función lo definimos al final de la misma, en la definición de los parámetros también se declara primero el nombre y después el tipo, y el acceso a arrays se realiza mediante paréntesis en vez de corchetes.

#Conclusiones

Lo más importante que podemos ver en este ejemplo, es que no necesitamos cambiar nuestra manera de programar para comenzar a usar Scala, y que podemos ir agregando características funcionales de lenguaje a nuestro código poco a poco.

En el siguiente artículo de la serie volveremos a la teoría, retomando por donde lo habíamos dejado, viendo características como las Case Classes, el reconocimiento de patrones, y otros ejemplos de código, más funcionales que podemos encontrar en GitHub.

Mientras tanto, aquí están algunos enlaces sobre coding katas que he usado para hacer este artículo:

Scala desde la perspectiva de C# y JavaScript, primera parte

Hace unos días me estuvieron hablando de las ventajas y maravillas de Scala, un lenguaje multiparadigma con un fuerte enfoque funcional que funciona sobre la máquina virtual de Java, es 100% compatible con el mismo en ambas direcciones, y también por ello es multiplataforma.

Tras echarle un vistazo a algo de documentación me pareció suficientemente interesante como para intentar escribir algo de código, y algunos artículos, usando la perspectiva de otros lenguajes como C# o JavaScript.

Historia

Scala es un lenguaje que llegó a nosotros desde el mundo académico por parte de Martin Odersky en el año 2003. Odersky, quien había trabajado anteriormente en javac, creó el lenguaje combinando ideas de diferentes lenguajes funcionales.

Como curiosidad, destacar que en sus primeras versiones estuvo disponible tanto para la plataforma Java como para .NET, aunque el soporte para el último fue perdiendo popularidad con los años y desde 2012 no está soportado oficialmente.

No es ni mucho menos el único lenguaje funcional que nos encontramos para la JVM, ya que existen alternativas como Haskell o Clojure, pero sin duda Scala es uno de los más potentes y veteranos, usado actualmente por empresas como LinkedIn, Twitter o FourSquare.

¿Qué tiene de especial?

Sintaxis simple

Basta con echar un vistazo a un “Hola mundo” para ver que la sintaxis es algo más simple que en java:

object HelloWorld {
	def main(args: Array[String]){
		println("Hello world")
	}
}

Si guardamos este código en un fichero HelloWorld.scala, podemos compilarlo usando scalac HelloWorld.scala y ejecutarlo ejecutar utilizando el comando scala -classpath . HelloWorld, con lo cual solamente necesitamos un bloc de notas (o Sublime Text/Atom/Vim/Emacs) y una línea de comandos para empezar a jugar.

Una de las cosas que primero nos llama la atención es la palabra reservada object. Al usar esta palabra reservada definimos la clase como Singleton y el compilador nos genera una instancia con el nombre de la clase, de tal manera que podríamos llamar al método HelloWorld.main desde cualquier punto de nuestra aplicación. Usando la palabra reservada class podemos utilizar las clases a las que estamos acostumbrados en otros lenguajes como C# y Java.

Todo es un objeto

A diferencia de Java, Scala no tiene el concepto de tipos básicos, como pudieran ser enteros, floats, o booleanos, sino que todos ellos son objetos, de la misma manera que lo podemos encontrar en C#. Como objetos, se pasan por referencia y no por valor, y simplifican tanto la lectura como la escritura, ya que no tenemos que estar trabajando con conceptos como boxing o unboxing.

Sin embargo, Scala va un paso más allá, y las funciones también pueden ser objetos, con lo cual las podemos pasar por referencia a otras funciones. Si hemos trabajado anteriormente con JavaScript, esto nos sonará mucho a los callbacks de métodos asíncronos, donde podemos pasar una función por parámetro, y si venimos de C# podemos conseguir el mismo efecto utilizando Delegates.

Veamos un ejemplo:

object Timer {
	def oncePerSecond(callback: () => Unit){
		while(true) {callback(); Thread sleep 1000 }
	}

	def callback(){
		println("Ping..."))
	}

	def main(args: Array[String]){
		oncePerSecond(callback)
	}
}

En este ejemplo podemos ver cómo utilizamos un callback que ejecutamos una vez cada segundo. Como curiosidad de sintaxis podemos ver que al llamar al método sleep no usa la sintaxis Thread.sleep(1000) a la que podíamos estar acostumbrados, que también es válida, lo que se denomina sintaxis infija.

Clases y constructores.

Siguiendo con la filosofía de no escribir más código del necesario, podemos definir nuestras clases en Scala directamente con el constructor en la definición de la misma, como muestra el ejemplo:

class Complex(real: Double, imaginary: Double){
	def re() = real
	def im() = imaginary
}

En este caso en la definición de la clase definimos además dos métodos que nos permiten recuperar el resultado de los valores. En caso de que necesitemos más de un constructor, Scala lo soporta mediante el uso de “this”, como muestra el siguiente artículo: Alternate constructors in Scala

La opción de definir los parámetros del constructor era algo que originalmente estaba previsto para C# 6, pero que se retiró de la especificación por su complejidad, aunque es posible que la veamos en C# 7 como muestra el hilo en CodePlex

Es importante destacar que Scala, a diferencia de JavaScript, es estáticamente tipado, con lo cual el tipo de dato de respuesta del método re, será de tipo Double, y se calculará en tiempo de compilación. Podríamos además, quitar los paréntesis tanto a los métodos re como im, y obtendríamos propiedades como las de C#

Resumen y siguientes pasos

Scala es un lenguaje multiparadigma, esto es, que podemos seguir programando de manera orientada a objetos, o podemos incorporar alguna de las características de la programación funcional. Utiliza la JVM y es compatible con Java en ambas direcciones.

En la segunda parte de esta serie, veremos características como las Case Classes, el reconocimiento de patrones, y algunos ejemplos de proyectos reales que podemos encontrar en github. Mientras tanto, aquí están algunas fuentes que he usado para hacer los artículos de esta serie, así como algunos vídeos recomendados:

Kanwal, mi propio Trello dentro del firewall

He de reconocer que soy un gran fan de Trello, un tablero de Kanban personalizable que me permite organizar mi agenda, mis objetivos personales, los viajes, las charlas, mis proyectos y los regalos de navidad, entre otros.

Sin embargo, para uso profesional he descubierto que muchas empresas que no permiten guardar información relacionada con proyectos en servicios en la nube, y para ello he creado un pequeño clon de Trello llamado Kanwal, para poderlo utilizar de manera segura dentro el firewall.

Para poder empezar a utilizarla necesitaba, como mínimo, las siguientes características:

  • Aregar listas
  • Agregar elementos a las listas
  • Borrar elementos de las listas

Características como mover un elemento de lista o editar elementos pueden ser muy chulas, aunque entrarán en la siguiente versión.

Con esa máxima tocaba escoger armas, y en mi caso fueron dos que ya había utilizado anteriormente:

  • Knockout.JS: Un framework MVVM muy sencillo y ligero que me permite realizar data-bindings de manera rápida y fácil
  • Bootstrap: Un framework CSS que me permite dar una estructura y un aspecto básicos a la web.
  • Yeoman: Una herramienta que nos permite crear proyectos con diferentes frameworks de manera rápida y sencilla.

Con las armas escogidas, tocaba ponerse manos a la obra, y un par de horas después la aplicación (si es que podemos llamarla aplicación) tenía este aspecto: Espartano, básico, pero es todo lo que necesito para empezar a trabajar:

kanwall-v1

La arquitectura es muy simple:

Una tarea:

  • Es una cadena de texto simple

Una lista:

  • Es un Viewmodel que contiene un título y una lista de tareas.
  • Contiene un método para agregar tareas, para borrarlas y para renombrar la lista, pasando a un sencillo “modo edición”.
  • Contiene una referencia al método guardar del viewmodel global.

El viemodel global:

  • Es una lista de listas.
  • Contiene Métodos para guardar y cargar los datos de local storage.

Como curiosidad, el nombre original era Kan-Wall (de Kanban firewall), aunque gracias a la corrección de Google descubrí que el nombre (con una L) correspondía a una región de Australia, así que así se ha quedado.

Le puedes echar un vistazo a la aplicación en rlbisbe.github.io/kanwal y ver el código fuente en github.
Si estás interesado en ayudar, envíame una pull request!

Have fun!