Constantes untyped vs. tipadas en Go

En Go, cuando escribes const X = 42, estás creando una constante untyped (sin tipo). Esto no significa que el valor no tenga una naturaleza matemática, sino que tiene un tipo ideal (ideal type): una representación pura que el compilador puede adaptar a cualquier tipo compatible en el momento en que se usa. Si, en cambio, escribes const X int = 42, estás definiendo una constante typed (tipada), lo que significa que el valor queda encadenado estrictamente al tipo int y no se moverá de ahí.

Esta distinción es lo que permite que la librería estándar de Go sea tan fluida. Gracias a las constantes untyped, puedes usar la constante Pi en una operación con float32 o en una con float64 sin necesidad de hacer conversiones manuales constantes; el compilador simplemente “ajusta” el valor ideal al tipo que el contexto requiere. Sin embargo, esta flexibilidad tiene un límite: si necesitas que el compilador te impida mezclar unidades lógicas (como sumar Celsius y Fahrenheit), debes usar constantes tipadas para forzar la seguridad del tipo.

Si te equivocas y usas constantes tipadas para cada número en tu código, terminarás lidiando con un ruido visual de conversiones explícitas como float64(miConstanteInt) en cada operación. Por el contrario, si abusas de las constantes untyped para representar dominios específicos, el compilador no podrá detectar errores lógicos donde mezclas conceptos distintos que comparten el mismo tipo base. Además, una ventaja crucial es que las constantes numéricas untyped poseen precisión arbitraria durante la compilación, permitiéndote trabajar con números de una magnitud o precisión que excedería los límites de un int64 o un float64 hasta que decidas asignarlos a una variable.

package main

import (
	"fmt"
)

// Definimos tipos personalizados para seguridad lógica
type Celsius float64
type Fahrenheit float64

func main() {
	// 1. Flexibilidad de las constantes untyped
	// Al no tener tipo, se adaptan al contexto automáticamente.
	const Dos = 2           // Untyped integer
	const Pi = 3.14159265    // Untyped float (con alta precisión)

	var x int = Dos         // Funciona: se adapta a int
	var y float64 = Pi      // Funciona: se adapta a float64
	var z float32 = Pi      // Funciona: se adapta a float32 sin pérdida visible

	fmt.Printf("Flexibilidad: %v (%T), %v (%T), %v (%T)\n", x, x, y, y, z, z)

	// 2. Precisión arbitraria en tiempo de compilación
	// Este número es demasiado grande para un int64 estándar,
	// pero el compilador lo maneja porque es untyped.
	const GranNumero = 123456789012345678901234567890
	fmt.Println("Precisión extrema:", GranNumero)

	// 3. Rigidez de las constantes typed
	// Al ser tipadas, el compilador exige exactitud.
	const UnEntero int = 10
	const UnFloat float64 = 5.5

	// fmt.Println(UnEntero + UnFloat) // Esto daría un error de compilación
	sumaManual := float64(UnEntero) + UnFloat
	fmt.Println("Suma manual (con cast):", sumaManual)

	// 4. Seguridad con tipos personalizados (Typed Constants)
	const TempCelsius Celsius = 25.0
	const TempFahrenheit Fahrenheit = 77.0

	// Aquí el compilador nos protege: no podemos sumar Celsius con Fahrenheit
	// aunque ambos sean float64 por debajo.
	// error := TempCelsius + TempFahrenheit // ERROR: invalid operation

	// Para operar, debemos ser explícitos, lo cual evita errores accidentales.
	total := float64(TempCelsius) + float64(TempFahrenheit)
	fmt.Println("Suma de temperaturas con conversión explícita:", total)
}

Desglose del ejemplo

En el código anterior, observa cómo Dos y Pi actúan como “comodines”. Cuando asignamos Dos a x (que es int), el compilador ve que el tipo ideal de Dos es compatible con int y hace la asignación sin que tú hagas nada. Lo mismo ocurre con Pi; como es una constante untyped, puede convertirse a float32 o float64 sin que el compilador se queje, manteniendo la precisión máxima permitida por el tipo destino.

Cuando definimos GranNumero, el compilador no lo almacena en una celda de memoria de 64 bits como una variable normal, sino que lo trata como un valor matemático puro durante la compilación, lo que permite manejar dígitos que un int64 normal perdería.

En la sección de seguridad, creamos los tipos Celsius y Fahrenheit. Al declarar TempCelsius como una constante tipada (const TempCelsius Celsius = 25.0), le estamos diciendo al compilador: “Este valor no es solo un número, es una temperatura en Celsius”. Esto hace que si intentas sumarlo con TempFahrenheit (que es de otro tipo), el compilador detenga la compilación. Aunque ambos sean float64 en su base, el sistema de tipos de Go te obliga a ser consciente de que estás mezclando conceptos distintos.

El error frecuente

Un error común es intentar realizar operaciones aritméticas entre constantes tipadas de distintos tipos básicos.

const A int = 10
const B float64 = 5.5

// Esto fallará al compilar:
// fmt.Println(A + B) 
// error: invalid operation: non-constant 10 (type int) can't be used in arithmetic with B (type float64)

Aunque A sea una constante, al haberle asignado el tipo int de forma explícita, dejas de tener la flexibilidad de la constante untyped. Para que el código anterior funcione, debes convertir una de las partes: float64(A) + B.

27

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio