El «Problema de la Expresión» (Expression Problem) es un desafío clásico en el diseño de lenguajes de programación. En este artículo exploraremos cómo abordar su resolución en Go utilizando genéricos, tomando como referencia las ideas presentadas en el artículo académico Featherweight Go y examinando las limitaciones de la implementación actual de Go.
Para comprender el desafío, resulta útil observar cómo lo define el artículo Featherweight Go:
«El objetivo es definir un tipo de datos por casos, donde se puedan añadir nuevos casos al tipo de datos y nuevas funciones sobre el tipo de datos, sin recompilar el código existente y manteniendo la seguridad de tipos estática».
El artículo ofrece una excelente analogía para ayudar a visualizarlo:
«El hecho de que un lenguaje pueda resolver el Problema de la Expresión es un indicador destacado de su capacidad expresiva. Se puede pensar en los casos como filas y en las funciones como columnas de una tabla. En un lenguaje funcional, las filas son fijas (los casos en una declaración de tipo de datos), pero es fácil añadir nuevas columnas (funciones). En un lenguaje orientado a objetos, las columnas son fijas (los métodos en una declaración de clase), pero es fácil añadir nuevas filas (subclases). Queremos que sea fácil añadir tanto filas como columnas».
En resumen: una verdadera solución nos permite añadir sin esfuerzo nuevas variantes de datos (filas) y nuevas operaciones o comportamientos (columnas) a nuestro código sin tener que modificar el código fuente existente.
La solución propuesta en el borrador original de Featherweight Go se basa en una característica que no llegó a incluirse en el lanzamiento final de los genéricos en Go 1.18.
En concreto, la solución ideal del artículo asume que se pueden aplicar restricciones de tipo más estrictas en el receptor de un método (method receiver) que las definidas en el struct base. Por ejemplo, lo ideal sería poder hacer esto:
// Hipotético (No es Go válido):
// Restringir directamente el receptor del método
func (e Plus[T Evaler]) Eval() int
func (e Plus[T Stringer]) String() string
Sin embargo, la implementación de genéricos en Go exige que las restricciones de tipo del receptor del método coincidan exactamente con la definición de tipos del struct. No es posible añadir nuevos requisitos de restricción (como Evaler o Stringer) método por método.
Dado que no podemos restringir los parámetros de tipo a nivel de método, nos vemos obligados a restringirlos a nivel de struct.
A continuación se muestra un ejemplo de código adaptado que funciona perfectamente con los genéricos de Go 1.18+. Sin embargo, observa que nos vemos forzados a definir de antemano una interfaz combinada Expr. Como debemos modificar esta interfaz cada vez que queramos añadir una nueva operación, esta no es una verdadera solución al Problema de la Expresión, sino más bien una solución práctica adaptada del artículo:
package main
import (
"fmt"
)
// 1. Definimos nuestro tipo de datos base
type Num struct {
value int
}
// 2. Definimos nuestras operaciones (Columnas)
type Evaler interface {
Eval() int
}
type Stringer interface {
String() string
}
// Implementamos las operaciones para nuestro tipo base
func (e Num) Eval() int {
return e.value
}
func (e Num) String() string {
return fmt.Sprintf("%d", e.value)
}
// 3. El compromiso: debemos predefinir una interfaz combinada.
// Si queremos añadir una nueva operación más adelante, estamos obligados a editar esta interfaz,
// violando la regla fundamental del Problema de la Expresión.
type Expr interface {
Evaler
Stringer
}
// 4. Definimos un tipo de datos genérico (Fila)
// Como Go no permite restricciones a nivel de método, debemos restringir T a Expr aquí.
type Plus[T Expr] struct {
left T
right T
}
func (e Plus[T]) Eval() int {
return e.left.Eval() + e.right.Eval()
}
func (e Plus[T]) String() string {
return fmt.Sprintf("(%s+%s)", e.left.String(), e.right.String())
}
// 5. Ejecución
func main() {
var e Expr = Plus[Expr]{left: Num{1}, right: Num{2}}
fmt.Println(e.Eval()) // Salida: 3
fmt.Println(e.String()) // Salida: (1+2)
}
Aunque los genéricos de Go son increíblemente potentes y aportan una flexibilidad muy necesaria al lenguaje, no resuelven de forma nativa el Problema de la Expresión tal y como lo concibió originalmente el artículo Featherweight Go. Al exigir que las restricciones se definan a nivel de struct en lugar de a nivel de método, los desarrolladores siguen teniendo que planificar las interfaces de antemano o modificar el código existente al añadir nuevas operaciones a tipos genéricos.
Obtén la edición sin conexión de El Estilo de Vida de Kaue—una guía práctica para optimizar 🍏 Salud, 💰 Riqueza, ❤️ Amor y 🧘 Sentido.
Recibe notificaciones cuando publique nuevos ensayos, resúmenes de libros y guías prácticas sobre cómo vivir bien. Sin spam—cancela cuando quieras.
¿Tienes alguna pregunta, perspectiva o sugerencia? Comparte tus ideas abajo. Los comentarios funcionan con GitHub Discussions (sin anuncios y sin spam)—haz clic en Iniciar sesión con GitHub abajo para comentar o reaccionar (crear una cuenta gratuita en GitHub toma menos de un minuto).
Actualizado el 11 oct 2026.
AVISO LEGAL: El contenido de este sitio tiene fines exclusivamente informativos y educativos y no constituye asesoramiento médico, psicológico, financiero, de inversión, fiscal ni legal. Las ideas, la selección y las opiniones aquí presentadas son exclusivamente mías y no representan las de mi empleador. Los resúmenes de libros, conferencias y artículos son comentarios educativos independientes y siguen siendo propiedad intelectual de sus respectivos autores. Se pueden utilizar herramientas de IA para ayudar en la edición, el formato y la síntesis; aunque se hace todo lo posible por garantizar la precisión, pueden producirse errores. PRIVACIDAD: Este sitio utiliza servicios de terceros para la suscripción opcional al boletín (follow.it), los comentarios (giscus / GitHub Discussions) y el contenido multimedia incrustado (YouTube), que pueden procesar las direcciones IP y las cookies de los visitantes de acuerdo con sus respectivas políticas de privacidad.