Go: El lenguaje de Google

Hace un par de años que escuché que Google trabajaba en un lenguaje, y hace poco que ya había visto la luz una versión estable, la primera.

Hay personas que me han comentado que Go era parecido a Erlang, que Go hacía concurrencia de forma muy simple, y que Go es un lenguaje muy a tener en cuenta y presente, sobretodo en terreno de concurrencia y escalabilidad. Hoy le he dedicado un par de horas a revisar la documentación y a programar un poco y ver cómo se programa en Go, las potencias que tiene el lenguaje según sus creadores (y lo que han podido introducir a lo largo del tutorial y el manual).

En 2007, un diseño inicial de Go fue comenzado por Robert Griesemer, junto a Rob Pike (creador del lenguaje Limbo) y Ken Thompson (uno de los creadores del lenguaje C y Unix), todos ellos bajo el seno de Google, donde los tres trabajan actualmente, por lo que, de momento, por su historia, promete.

Revisión del lenguaje

En principio, Go es un lenguaje tipado y fuertemente tipado. Tiene auto-detección de tipos para no tener que escribir mucho y han organizado su declaración de modo que resulta un poco extraño para quien haya programado en C y Java, por ejemplo. Mientras que un código en estos lenguajes declara el tipo y modificadores para después indicar el nombre de la variable, Go lo hace al revés. Se indica primero el nombre de las variables y después el tipo. Además, el tipo puede tomarse de forma intrínseca si se hace una inicialización con un valor. Por ejemplo:

var x int  // declaración simple
var y, z int  // declaración múltiple, ambas serán int
var a := 1  // declaración intrínseca a int
var a, b := 1, 2 // declaración intrínseca de a y b a int

En este aspecto, recuerda bastante a Python, pudiendo hacer asignación múltiple en una misma línea. La palabra var es necesaria en la declaración de las variables de modo global, y solo opcional dentro de las funciones. Un dato de tipo intrínseco dentro de las funciones, a menos que se emplee var, debe de indicarse con el operador :=

La declaración de funciones se realiza con la palabra clave func, pudiendo aceptar parámetros tipados de la misma forma que la declaración de variables. El retorno, al igual que la asignación, se realiza de modo que se puedan retornar varios valores, igualmente, otro parecido más a Python en este sentido:

func add( x, y int ) int {
    return x + y
}

En parecido con C, para que el código sea ejecutado, debe de existir una función main.

Como estructuras de control, tenemos if, for y switch. El bucle while se puede simular a través del uso simplificado de for, que permite eliminar la parte de inicialización y actualización para realizar son la condición de bucle. Igualmente, si no se indican ninguna de las tres, se realiza un bucle infinito.

for i := 1; i<10; i++ {
    // bucle for normal
}
 
for i < 10 {
    // simulación de while
}
 
for {
    // bucle infinito
}

Las estructuras if permiten el uso de la parte de inicialización, como si se tratase de un bucle for, y las estructuras switch permiten declarar comparadores, no solo valores estáticos como en Java, C, PHP y otros, además de también el bloque de inicialización como if:

if i == 10 {
    // if normal
}
 
if i=10; i==10 {
    // if con inicialización
}
 
switch i=10; i {
    case 1: // si es 1
    case 2: // si es 2
    case 10: // aqui entra
    default: // por defecto
}
 
switch {
    case i == 1: // si es 1
    case i == 2: // si es 2
    case i > 3: // aqui entra
    default: // por defecto
}

La organización de las estructuras de datos se puede hacer mediante struct, al igual que en C, e incluso se pueden definir funciones que trabajen como si uno de sus parámetros intrínsecos fuese un struct. Con esto se puede simular el uso de los métodos, pero queda muy lejos de lo que respecta a la programación orientada a objetos.

Se pueden ver más ejemplos en tutorial de Go (en inglés).

Potencias del lenguaje

Revisándolo a fondo, veo que la tendencia de los lenguajes de tipado estático comienza a estar de moda, tienen sus ventajas ya que permiten cazar fallos en tiempo de compilación (cuando no hay inferencia de tipos, claro). En este caso esa cualidad es bastante evidente, así como que el código pueda compilarse de forma nativa como hace también Opa.

La concurrencia que emplea Go se parece bastante a Erlang en base en que es por paso de mensajes. La única diferencia radica en que el buzón para el paso de mensajes se debe de crear. Go nomina sus buzones como canales de comunicación. Un símil muy aproximado a los sockets Unix, ya que se establecen como recursos independientes y se pueden emplear en cualquier proceso y en cualquier momento.

Conclusiones

El lenguaje Go es un lenguaje que ha nacido con un propósito claro, según sus objetivos entre los que se encuentran: seguridad (de tipos y de memoria), buen soporte de concurrencia y comunicación, eficiencia en la recolección de basura (sin latencias) y alta velocidad de compilación.

Viniendo de dos de los trabajadores de los laboratorios Bell que contribuyeron a lenguajes como Limbo, C, BPEL, etc. y estando en el seno de Google, el futuro de este lenguaje se ve muy prometedor.

Este lenguaje permite la programación orientada a la concurrencia o modelo actor, con lo que, la orientación a objetos, realmente, no es una característica que se eche en falta, ni mucho menos, siempre teniendo presente que el paradigma de programación al que se orienta no son los objetos, en su núcleo central, sino los procesos (o hilos).

Cabe destacar que el uso de la memoria compartida es un hecho ya que el comando go lo que hace es lanzar, como dice el tutorial goroutines, que a nivel interno son hilos, por lo que comparte el mismo heap de memoria y puede llegar a ser un problema en el uso de la concurrencia y el modelo actor propiamente dicho.

Por otro lado, carece de estructuras que faciliten el uso de monitores de hilos a modo de que cuando uno muera se notifique este hecho a otro y poder construir estructuras como las de los supervisores.

Veremos como va progresando a lo largo de las siguientes liberaciones, de momento son buenos pasos los que ha dado el pequeño, y con la promesa de Google de mantener la sintaxis y compatibilidad en sus futuras versiones, es una seguridad de cara a programar y no tener que refactorizar al cambiar a una nueva versión.