En 2009, Google creó un nuevo lenguaje de programación. Veamos por qué era necesario y qué es lo que a los programadores les gusta de Go.

Un día en Google, decidieron crear una alternativa cómoda y potente a C++. Así apareció Golang, que ocupa de forma estable posiciones altas en las clasificaciones de lenguajes de programación y atrae a nuevos desarrolladores.

El lenguaje fue creado por Rob Pike y Ken Thompson. Ambos son figuras icónicas en la informática y antiguos empleados de la legendaria Bell Labs. Y Thompson, además, es uno de los creadores del sistema operativo UNIX y del lenguaje B (predecesor de C).

¿Qué es el lenguaje de programación Go?

Go, o Golang, es un lenguaje compilado, multiproceso y de código abierto. Principalmente se utiliza en servicios web y aplicaciones cliente-servidor. A finales de 2023, Golang incluso entró en el top 3 de los lenguajes más solicitados y superó a PHP, C++ y TypeScript.

Los autores del lenguaje intentaron combinar la facilidad de desarrollo en Python con la velocidad de ejecución de programas en C y C++, por lo que hicieron Go compilado. Y aunque en el ecosistema de Go existe su propio intérprete, rara vez es necesario. El código se compila rápidamente.

En general, con Go se puede programar cualquier cosa: desde aplicaciones de consola hasta programas complejos multiproceso. Pero es más adecuado para aplicaciones de servidor. Incluso existen bibliotecas para crear interfaces gráficas, aunque programar con ellas es muy engorroso.

Características del lenguaje Go

El lenguaje está diseñado para que los desarrolladores se centren en la arquitectura de las aplicaciones y no pierdan el tiempo en tareas tediosas, como crear documentación o rastrear construcciones sintácticas obsoletas. Go es fácil de usar y es bueno precisamente porque la ejecución de todas las operaciones rutinarias se ha trasladado del programador a las herramientas integradas.

Las principales características de Go son:

  • Gestión automática de memoria y recolector de basura. Go es rápido, como C/C++, pero programar en él es más fácil. Si en C/C++ hay que gestionar la memoria manualmente, el compilador de Golang se encarga de estas tareas.
  • Sintaxis simplificada. Son concesiones sintácticas que permiten escribir código más rápido. Por ejemplo, formalmente algunas operaciones en Go (if, for) deben terminar con punto y coma, pero en realidad el compilador es capaz de colocar los puntos y comas en los lugares necesarios.
  • Formato automático de programas. Golang coloca automáticamente las sangrías y alinea los elementos en columnas con el comando gofmt. Pero es importante usar sólo tabulaciones para la sangría de las líneas; gofmt no entenderá los espacios al principio de la línea.
  • Creación automática de documentación. El comando godoc encuentra todos los comentarios y crea un manual de la aplicación a partir de ellos.
  • Seguimiento de construcciones obsoletas. La herramienta gofix analiza el código y marca las construcciones sintácticas que se consideran obsoletas según los estándares modernos.
  • Herramientas de prueba. Go incluye muchas herramientas de prueba. Por ejemplo, typecheck comprueba la correspondencia de tipos en el código, golint da recomendaciones basadas en la documentación oficial (Effective Go y CodeReviewComments), gosimple simplifica las construcciones sintácticas complejas y gas encuentra vulnerabilidades en el código.
  • Seguimiento del estado de la carrera. Para trabajar con sistemas multiproceso, es muy importante ejecutar las funciones en el orden correcto para no mezclar los datos, porque el estado de la carrera es un error muy peligroso. Puede producirse de forma aleatoria, por lo que es casi imposible localizarlo. Golang está diseñado desde el principio para minimizar estos errores. Y si algo se escapa, existen herramientas adicionales para comprobar el código en busca de un estado de carrera. Para activar el detector de carrera, hay que añadir el indicador —race en la fase de compilación, construcción, prueba o instalación del paquete.
  • Perfilado. El lenguaje de programación Go tiene el paquete pprof y la utilidad de consola go tool pprof. El perfilador pprof investiga qué fragmentos de código se ejecutan demasiado tiempo, dónde el programa consume mucha memoria o sobrecarga demasiado el procesador. El resultado de su trabajo es un informe de texto, un perfil. Para visualizar el perfil y construir un diagrama a partir de él, hay que instalar la utilidad graphviz.
Gráfico generado por Graphviz que muestra la estructura jerárquica de datos.
Análisis de datos mediante representación gráfica.
  • Programación de bajo nivel. Por supuesto, Go no podría competir con C y C++ si no pudiera trabajar directamente con la memoria. Para eso, tiene el paquete unsafe.

Posibilidades técnicas de Go

Una de las propiedades importantes de Go es la multitarea. Aquí habrá que profundizar un poco en la historia de las tecnologías informáticas.

En 1965, Gordon Moore, fundador de Intel, formuló una ley: cada dos años se duplicará el número de transistores en un circuito integrado. No hay datos científicos ni fórmulas detrás, sólo una observación. Y hasta el siglo XXI, la ley de Moore funcionó correctamente. Pero aproximadamente después del Pentium 4 quedó claro: un poco más, y los procesadores se calentarán como una supernova. Entonces los fabricantes empezaron a fabricar procesadores multinúcleo: la frecuencia de reloj y el número de transistores casi no cambiaron, pero el rendimiento total aumentó.

Para utilizar todas las posibilidades de estos procesadores, los programas deben escribirse teniendo en cuenta la multinuclearidad. En Go, existen entidades especiales para ello: las goroutines y los canales.

Goroutines

Son funciones que pueden funcionar en paralelo, es decir, el programa ejecuta varias líneas casi simultáneamente. Para convertir una función en una goroutine, basta con escribir go antes.

Así es como se ve:

func server(i int) {
    for {
        print(i)
        time.Sleep(10)
    }
}
go server(1)
go server(2)

El resultado es una llamada prácticamente simultánea, a pesar del retraso time.Sleep(10), de ambas goroutines. Por supuesto, en un programa pequeño esto es prácticamente inútil, pero al llamar a muchas funciones es muy justificado. Se ahorra tiempo y los recursos del procesador se utilizan de forma uniforme.

La ejecución de las goroutines en Go la controla una biblioteca especial de tiempo de ejecución: distribuye los núcleos del procesador entre ellas, puede limitar el número de núcleos disponibles. La biblioteca ayuda a lanzar una gran cantidad de goroutines, muchas más de las que permite el sistema operativo, y no requiere que el programador se ocupe de la paralelización manualmente.

Channels (Canales)

Es una especie de almacén de datos común. Los canales se pasan como argumentos de las goroutines y les ayudan a comunicarse entre sí e intercambiar datos. Los canales tienen una cola y un bloqueo, para que diferentes goroutines no puedan introducir datos diferentes simultáneamente. La peculiaridad de los canales: permiten escribir y leer sólo un tipo de datos. Por ejemplo, int: números enteros.

package main

import "fmt"

func main() {
    channel := make(chan float32)

    fmt.Printf("type of 'c' is %T\n", channel)
    fmt.Printf("value of 'c' is %v\n", channel)
}

Ejecutar en play.golang.org

Se parece un poco al trabajo con variables: usamos el operador de asignación y establecemos inmediatamente el tipo de datos. Pero es interesante que el valor del canal será su dirección en la memoria (salida del segundo operador Printf).

Ahora combinemos las goroutines y el canal:

package main

import "fmt"

func gorutine_test(channel chan string) {
    fmt.Println("Hey, " + <-channel + "!")
}

func main() {
    fmt.Println("main() started")
    channel := make(chan string)

    go gorutine_test(channel)

    channel <- "Rob"
    fmt.Println("main() stopped")
}

Ejecutar en play.golang.org

Y ahora, observen: vamos a analizar el código:

  • Declaramos la función gorutine_test con el argumento channel. El resultado de su trabajo es una cadena de saludo y los datos del canal; los leemos con el operador <−.
  • La función main primero muestra en pantalla un mensaje de que ha comenzado.
  • A continuación, creamos el canal channel y le asignamos el tipo de datos string.
  • Ahora lanzamos la función gorutine_test como una goroutine y le colocamos el canal channel.
  • Ahora main y gorutine_test están activos.
  • Ahora colocamos en el canal el nombre del creador del lenguaje de programación Go: Rob. La función main se bloquea inmediatamente hasta que gorutine_test lea los datos del canal. Observen que gorutine_test se llama antes de que enviemos el valor al canal, pero el planificador de Go lo ejecuta.
  • Después de esto, la función main se desbloquea y muestra un mensaje de que ha terminado su trabajo.

La multitarea encaja perfectamente con el paradigma de programación funcional, y el lenguaje Go lo apoya en gran medida. Por supuesto, contiene construcciones imperativas, elementos de OOP y todo eso. Pero es la paradigma funcional claramente expresada la que hace de Golang una herramienta poderosa para soluciones de servidor de alta carga, servicios y cálculos complejos.

Tipos de datos en Go

Go es un lenguaje con tipado estático estricto, es decir, cada variable tiene su tipo y no se puede cambiar. Comparemos con PHP:

<?php
$foo = "1";  // $foo es una cadena (código ASCII 49)
$foo *= 2;   // $foo ahora es un entero (2)
$foo = $foo * 1.3;  // $foo ahora es un número de coma flotante (2.6)
$foo = 5 * "10 Little Piggies"; // $foo es un entero (50)
$foo = 5 * "10 Small Pigs";     // $foo es un entero (50)

$a = "1.5"; // $a es una cadena
$b = 100; // $b es un entero
$c = $a * $b; // $c es un número de coma flotante, valor — 150.0
?>

En el ejemplo, cambiamos el tipo de datos sobre la marcha e incluso realizamos operaciones matemáticas con una cadena y un entero. En Go, esto es imposible: si una variable se declara como un entero, seguirá siéndolo durante toda la ejecución del programa; sólo se puede cambiar su valor. Y si intentamos colocar datos de otro tipo, el módulo de comprobación de Go nos indicará que tenemos un error.

El lenguaje de programación tiene 11 tipos de enteros. Se diferencian por el número de bits, las características (por ejemplo, existe un tipo byte para los números binarios) y el contexto (por ejemplo, uintptr para trabajar con código externo). Además, existen números de coma flotante, números complejos, números booleanos, cadenas y tres tipos de números de precisión ilimitada, que pueden tomar cualquier valor y están limitados sólo por la memoria del ordenador.

Las variables en Go se declaran al estilo Pascal, mediante el operador var, y la propia declaración se puede combinar con la asignación:

var v1 int // Declara la variable v1 y le asigna el tipo «entero»
var v2 string = "teach Go, friend" // Declara la variable v2, le asigna el tipo «cadena» y le asigna el valor «teach Go, friend»
v1 := v2 // Lo mismo que var v1 = v2, declara la variable y le asigna un valor
  • En la primera línea declaramos la variable v1 y le asignamos el tipo «entero»;
  • en la segunda línea, declaramos la variable v2, le asignamos el tipo «cadena» y le asignamos el valor teach Go, friend;
  • en la tercera línea, hacemos lo mismo que v1 = v2, declaramos la variable y le asignamos un valor.

El operador de asignación en Go es el signo =:

a = b
i, j = j, i

Aquí, en la primera línea, asignamos a la variable a el valor de b, y en la segunda, intercambiamos los valores de i y j.

Cómo instalar y empezar a usar el lenguaje de programación Go

Se puede descargar Go para diferentes plataformas en el sitio web oficial: existen versiones precompiladas para Windows, macOS y Linux. También se pueden compilar los códigos fuente en muchas plataformas: FreeBSD, OpenBSD, DragonFly BSD, Solaris, Android, AIX, Plan 9 (por cierto, también es obra de Thompson y Pike, con un nombre que hace referencia a la película de Ed Wood, el fracasado más famoso de la fábrica de sueños).

Para comprobar si Go se ha instalado correctamente en Windows, introduzca el comando go en la línea de comandos.

go

Escribir código en Go se puede hacer en programas de tres tipos, según lo que mejor te convenga:

  • Un editor de texto con resaltado de sintaxis Go, autocompletado, compilación y depuración. Normalmente se implementan mediante plugins, por ejemplo, en Notepad++, Vim, Emacs.
  • Un entorno de desarrollo integrado (IDE): Eclipse, NetBeans, IntelliJ IDEA, Komodo, Codebox, Visual Studio, Zeus IDE y otros.
  • Un entorno de desarrollo especializado para Golang. Los más conocidos son GoLand de JetBrains (comercial) y LiteIDE (open source).

Primer programa en Go

Por tradición, es, por supuesto, ¡Hola, mundo!. A continuación, se analiza la sintaxis:

package main

import "fmt"

func main() {
    fmt.Println("Hola, Mundo!")
}
  • package main — damos nombre al paquete; es necesario para los archivos que se van a ejecutar;
  • import 'fmt' — llamamos al paquete que se encarga del formateo y la salida de información (estos paquetes también se denominan bibliotecas);
  • func main() — cada archivo ejecutable debe incluir la función principal: main;
  • fmt.Println(«Hola, Mundo!») — llamamos a la función Println del paquete fmt; muestra la información entre paréntesis en la pantalla;
  • «Hola, Mundo!» — las comillas indican que hay que mostrar como cadena todo lo que hay dentro;
  • // — así es como se indican los comentarios de una sola línea; el compilador Go ignora todo lo que viene después de este símbolo hasta el final de la línea.

Ahora hagamos algo más complejo. Escribamos una función que devolverá los números de Fibonacci:

package main

import "fmt"

func fib() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    f := fib()
    fmt.Println(f(), f(), f(), f(), f())
}
  • Las dos primeras líneas son exactamente iguales que en el ejemplo anterior.
  • func fib() func() int — declaramos una función que devolverá el siguiente número de Fibonacci. Se llama fib y devuelve otra función: func () int. Esto es necesario para que cada vez que se llame obtengamos el siguiente número de Fibonacci.
  • a, b: = 0, 1 — creamos dos variables y les asignamos los valores 0 y 1 respectivamente. Esto es necesario para calcular el siguiente número de Fibonacci.
  • return func() int — devolvemos la función.
  • a, b = b, a+b — calculamos el siguiente número de Fibonacci.
  • return a — devolvemos el siguiente número de Fibonacci.
  • func main — llamamos a la función principal main.
  • f: = fib() — creamos la variable f y colocamos en ella la función que devolverá el siguiente número de Fibonacci cada vez que se llame.
  • fmt.Println (f(), f(), f(), f(), f()) — mostramos los cinco primeros números de Fibonacci.

En resumen

Go es un lenguaje de programación potente, elegante y moderno, con una velocidad comparable a la de C y C++, y una facilidad de creación de código comparable a la de Python. Incluso un principiante puede dominarlo.

Golang tiene una documentación sencilla y concisa y una comunidad amigable donde siempre se puede hacer una pregunta; un programador experimentado lo aprenderá rápidamente como segundo idioma. Las perspectivas son muy serias, para mucho tiempo: el lenguaje está respaldado por Google, pero vive como un proyecto independiente y libre con código abierto.

Categorizado en:

Go, Programación,