3 puntos por GN⁺ 2024-01-05 | 1 comentarios | Compartir por WhatsApp
  • Desde que Go se publicó como código abierto el 10 de noviembre de 2009, durante 14 años evolucionó con el objetivo no tanto de ser un lenguaje nuevo en sí, sino de ofrecer una forma de facilitar la creación de software de producción
  • La especificación formal, múltiples implementaciones de compiladores, la compilación cruzada sencilla, la garantía de compatibilidad desde Go 1.0, la biblioteca estándar y gofmt respaldaron su adopción
  • La concurrencia para estructurar software de servidor fue una fortaleza, pero al principio no se explicó lo suficiente la diferencia con el paralelismo ni sus casos de uso adecuados, lo que generó confusión
  • Las interfaces se convirtieron en la base de las bibliotecas, las pruebas, la gestión de dependencias y el diseño centrado en la composición; la ausencia de genéricos dificultó durante más de una década el diseño polimórfico
  • En gestión de paquetes, documentación y colaboración comunitaria hubo mucho ensayo y error, pero Go apunta a un entorno donde el código se ve parecido y sigue compilando aunque cambien sus autores y pase el tiempo

El objetivo de Go: una forma de desarrollar software más que un lenguaje

  • El 10 de noviembre de 2023 se cumplieron 14 años desde que Go se publicó como proyecto de código abierto
  • En el momento de su publicación en 2009, Ken Thompson, Robert Griesemer, Russ Cox, Ian Taylor, Adam Langley, Jini Kim y otros presenciaron el lanzamiento del proyecto
  • El objetivo original de Go no era un nuevo lenguaje de programación en sí, sino escribir software de alta calidad de una mejor manera
  • En aquel entonces, el cuello de botella estaba menos en las funciones del lenguaje y más en la complejidad del proceso de crear software de servidor moderno en Google
    • Control de dependencias
    • Equipos grandes y estructuras de personal cambiantes
    • Facilidad de mantenimiento
    • Pruebas eficientes
    • Aprovechamiento de CPU multinúcleo y redes
  • Go todavía se define como un proyecto para hacer más fácil y productiva la construcción de software de producción

La identidad comunitaria creada por la mascota Gopher

  • El gopher de Go fue uno de los factores iniciales del éxito de Go
  • El gopher creado por Renee French funciona como símbolo compartido por los programadores de Go y como bandera de la comunidad
  • Al igual que las conferencias llamadas GopherCon, el gopher ayudó a crear una atmósfera de proyecto donde conviven la excelencia técnica y la diversión
  • La decisión de publicarlo bajo una licencia Creative Commons Attribution tuvo pros y contras
    • Fomentó remixes y variaciones, fortaleciendo el espíritu comunitario
    • La condición de atribución también generó debates o créditos incorrectos en trabajos que no habían sido creados por Renee French
  • Mantener la mascota fiel a su espíritu original sigue siendo una tarea difícil

Decisiones técnicas que Go hizo bien

  • Se escribió primero una especificación formal para fijar el comportamiento de las implementaciones de compiladores y permitir que varias implementaciones coincidieran en el mismo comportamiento
    • Se partía de la idea de que un solo compilador no podía ser la especificación
    • El primer borrador de la especificación se escribió en el piso 18 de un edificio en Darling Harbour, Sídney
  • Tener múltiples implementaciones de compiladores ayudó a pulir el lenguaje y la especificación
    • Ian Taylor envió un correo el 7 de junio de 2008 diciendo que había leído el borrador de la especificación de Go y creado un frontend para gcc
    • Más adelante también aparecieron varias implementaciones compatibles
  • Al facilitar la compilación cruzada, los desarrolladores pudieron trabajar en la plataforma que quisieran y desplegar en la plataforma necesaria
  • La decisión de limpiar el lenguaje en Go 1.0 y luego introducir una garantía de compatibilidad tuvo un gran impacto en la adopción de Go
    • Mantener una compatibilidad fuerte tiene costos, pero reduce el temor de que una nueva versión de Go rompa un proyecto
  • La biblioteca estándar ofreció la mayoría de las funciones necesarias para escribir código de servidor del siglo XXI e hizo que la comunidad trabajara sobre la misma caja de herramientas
    • Al principio, la biblioteca creció en parte porque no había otro lugar donde instalar código Go
    • Ayudó a reducir la proliferación de bibliotecas variantes y a unificar la comunidad

La experiencia de desarrollo creada por las herramientas y gofmt

  • Go se diseñó como un lenguaje fácil de parsear, lo que facilitó la creación de herramientas
  • Al principio se creía que hacía falta un IDE, pero a medida que la instrumentación se volvió más sencilla, con el tiempo los IDE incorporaron soporte para Go y también apareció gopls
  • Junto con el compilador se entregaron herramientas auxiliares como pruebas automáticas, cobertura y vetting de código
  • El comando go integró todo el proceso de compilación, y muchos proyectos pudieron resolver con ese comando la mayor parte de lo necesario para compilar y mantener código Go
  • gofmt se consolidó como una herramienta que influyó no solo en Go, sino en toda la comunidad de programación
    • Robert Griesemer insistió desde el comienzo en crear gofmt
    • Antes de gofmt, los formateadores automáticos no tenían mucha calidad y casi no se usaban
    • Se redujo el tiempo dedicado a discutir sobre espacios y saltos de línea
    • Al convertir el interior de gofmt en una biblioteca, fue posible parsear y modificar el AST y volver a producir una salida legible para humanos
    • También se hicieron posibles otras herramientas como simplifiers, analyzers y herramientas de cobertura de código

Concurrencia: una función poderosa, pero explicada tarde

  • En 2002, el software de Google evitaba casi por completo los threads, y había un ambiente que desalentaba su uso citando la postura de John Ousterhout de que “threads were bad”
  • Go partió de la experiencia de que era posible usar concurrencia de una mejor manera que con paquetes de bajo nivel como pthread
  • Go desempeñó un papel importante para que el mundo de la programación aceptara la concurrencia como una herramienta poderosa
    • Fue especialmente efectiva en entornos multinúcleo y de red
    • Hoy, la mayoría de los lenguajes principales cuentan con soporte para concurrencia
  • Go no usó nombres como coroutine, task o thread, sino que creó el nuevo término goroutine
  • async/await es una gran mejora frente a pthread, pero comparado con goroutine, channel y select, aunque puede ser más fácil y pequeño para quien lo implementa, también puede devolver parte de la complejidad al programador
    • El problema de Bob Nystrom, “What Color is Your Function?”, se usa como ejemplo
    • El modelo CSP de Go encaja bien con lenguajes procedurales, pero para implementarlo correctamente se requiere complejidad en el runtime
    • Como puede ser problemático tener varias implementaciones de concurrencia en un mismo entorno, Go la incorporó al lenguaje y no a una biblioteca

Dos errores con la concurrencia

  • El equipo de Go no orientó lo suficiente al principio sobre dónde debía usarse la concurrencia
    • Su principal caso de uso era simplificar el software de servidor en bibliotecas centrales como net/http
    • No era una función que hubiera que usar en todas partes de todos los programas
  • También fue un problema haber explicado tarde la diferencia entre paralelismo y concurrencia
    • Muchos desarrolladores intentaron paralelizar su código con goroutines para hacerlo más rápido, pero se sorprendieron al ver que se volvía más lento
    • El código concurrente solo puede acelerarse mediante paralelización cuando el problema en sí es paralelo, por ejemplo al procesar solicitudes HTTP
  • Para corregir esto, en Waza 2012 se presentó la charla “Concurrency is not Parallelism
  • Esa explicación debería haberse dado antes
  • Aun así, Go contribuyó a popularizar la concurrencia como forma de estructurar software de servidor

Interfaces y diseño centrado en la composición

  • Las interfaces son, junto con la concurrencia, una de las ideas centrales que distinguen a Go
  • Las interfaces de Go se acercan a la respuesta de Go al diseño orientado a objetos original, centrado en el comportamiento
  • El enfoque dinámico, en el que no hace falta declarar de antemano qué tipo implementa qué interfaz, recibió críticas al principio, pero fue importante para la programación al estilo Go
  • La biblioteca estándar, las pruebas y la gestión de dependencias dependen en gran medida del carácter abierto de las interfaces
  • sort de Go está implementado como una función que opera sobre una interfaz
    • Es diferente del estilo orientado a objetos al que mucha gente estaba acostumbrada, pero fue una idea poderosa
  • Interfaces pequeñas como Reader, Writer y la interfaz vacía se convirtieron en la base de las bibliotecas de Go
    • Estas tres interfaces famosas tienen, en promedio, 2/3 métodos cada una
    • Los conjuntos pequeños de métodos se volvieron un modismo propio de Go

El largo desvío alrededor de los genéricos

  • El equipo de Go había visto que la programación genérica tendía a hacer que se pensara en los tipos antes que en los algoritmos
    • Preferían el diseño orgánico por encima de la abstracción temprana
    • Daban más importancia a las funciones que a los contenedores
  • Go incluyó en el lenguaje contenedores genéricos como map, slice, array y channel, pero no expuso directamente esa genericidad a los usuarios
  • Se consideró que esos tipos bastaban para la mayoría de las tareas de programación simples, pero también había casos en los que no, y ese límite resultó incómodo para algunos usuarios
  • Como las interfaces eran la base de la programación en Go, el nuevo modelo de polimorfismo tenía que encajar bien con ellas
    • Hubo varios intentos e implementaciones abandonadas
    • Tras muchas discusiones, también recibieron ayuda de teóricos de tipos liderados por Phil Wadler
  • Finalmente, Go diseñó los genéricos generalizando las interfaces de “conjuntos de métodos” a “conjuntos de tipos”
  • El término “generics” proviene del polimorfismo centrado en estructuras de datos, por lo que no describe con precisión el polimorfismo paramétrico que ofrece Go, pero en la práctica se terminó usando la palabra generics

Compilador: primero en C, después en Go

  • Que el compilador inicial de Go estuviera escrito en C incomodó a parte de la comunidad del lenguaje
    • Había opiniones de que debía usarse un toolkit como LLVM o que el propio Go debía ser self-hosting
  • La razón para escribir el primer compilador en C fue práctica
    • Para hacer bootstrap de un nuevo lenguaje hacía falta un lenguaje existente
    • Ken Thompson ya había escrito un compilador de C, y su estructura interna podía servir como base para el compilador de Go
    • Si se diseñaba el lenguaje y al mismo tiempo se escribía el compilador en ese lenguaje, se corría el riesgo de sesgarlo hacia un lenguaje bueno para escribir compiladores
  • El compilador inicial generaba código común y su interior no era muy pulcro, pero era pequeño, familiar y fácil de cambiar rápidamente
  • La incorporación de segmented stacks que crecían automáticamente mostró la ventaja de esa elección
    • Si se hubiera usado un toolkit como LLVM, habría sido difícil integrarlo porque también habría que cambiar la ABI y el soporte del recolector de basura
  • La estructura proveniente originalmente de la familia de compiladores de Plan 9 también encajaba bien con la compilación cruzada
  • En Go 1.5, Russ Cox escribió una herramienta para traducir semiautomáticamente el compilador de C a Go
    • Para ese momento, el lenguaje ya estaba completo y había desaparecido la preocupación de que el diseño estuviera centrado en el compilador
    • Tras migrarlo a Go, las ventajas de Go —pruebas, herramientas, reescrituras automáticas y análisis de rendimiento— pudieron aplicarse también al desarrollo del compilador
  • Hoy existen compiladores de Go basados en LLVM y varios otros compiladores

Operación del proyecto de código abierto y comunidad

  • Desde el principio se consideró que Go tenía que ser un proyecto de código abierto para tener éxito
  • Sin embargo, se pensó que sería más productivo desarrollarlo en privado hasta ordenar las ideas centrales y la implementación del comportamiento
    • Los primeros 2 años antes de la publicación fueron importantes para aclarar los objetivos
  • Tras pasar a código abierto, los aportes de la comunidad fueron abrumadores, e Ian Taylor en particular dedicó mucho tiempo a responder preguntas
  • El port a Windows fue realizado completamente por la comunidad bajo la guía de Alex Brainman
  • Al equipo de Go le tomó mucho tiempo entender cuál era la mejor forma de colaborar con la comunidad
    • Creían estar comunicándose bien, pero se perdió tiempo por malentendidos y expectativas desalineadas
  • El proyecto Go eligió no aceptar código rápidamente para luego limpiarlo, sino asegurar una alta calidad de código antes de hacer merge
    • Revisión de código obligatoria
    • Atención minuciosa a los detalles
  • Este enfoque impone más carga a la comunidad, por lo que, si los participantes no entienden su valor, pueden sentir que no son bienvenidos
  • El historial del repositorio del proyecto Go pasó por SVN, Perforce, Mercurial y Git, pero Russ Cox mantuvo el historial, de modo que el repositorio Git actual conserva registros de cambios desde el SVN inicial
  • Google apoya Go, pero no fija la agenda del equipo de Go
    • La gran base de código Go interna de Google se usa para probar y validar lanzamientos
    • El flujo va del repositorio público hacia Google, no al revés
    • Google paga los sueldos del equipo central de Go, pero este actúa de forma independiente

Ensayo y error en la gestión de paquetes

  • El diseño del package del lenguaje Go en sí fue excelente, pero el proceso de desarrollo de la gestión de paquetes no avanzó bien
  • Especificar rutas como cadenas simples en las sentencias import fue una decisión importante y flexible
  • El problema fue que la transición desde un estado donde solo existía la biblioteca estándar hacia una forma de traer código desde la web fue accidentada
  • El equipo inicial de Go estaba acostumbrado al monorepo de Google y a compilar desde head
    • No tenía suficiente experiencia usando gestores de paquetes con múltiples versiones de paquetes
    • No abordó desde el comienzo la complejidad técnica de resolver grafos de dependencias
  • El trabajo en deps.dev se trató como una especie de compensación por este problema
  • Los intentos de resolver la gestión de dependencias junto con la comunidad tuvieron buenas intenciones, pero cuando apareció el diseño final, muchos integrantes de la comunidad se sintieron excluidos
  • Esta experiencia dejó lecciones sobre cómo debe darse realmente la participación comunitaria, y desde entonces se mejoró mucho
  • Actualmente, el diseño se estabilizó, es técnicamente excelente y parece funcionar bien para la mayoría de los usuarios

Documentación y ejemplos ejecutables

  • Al principio se escribió mucha documentación, pero no estaba al nivel que la comunidad quería
  • La principal carencia era que incluso las funciones más simples necesitaban ejemplos de uso
    • Mostrar cómo se usa algo era más valioso que decir qué hace
  • La documentación actual incluye muchos ejemplos, la mayoría aportados por contribuidores de código abierto
  • Go hizo que los ejemplos pudieran ejecutarse en la web desde etapas tempranas
    • En una presentación de Google I/O de 2012, se pudieron ejecutar en el navegador fragmentos de código que mostraban concurrencia
    • Andrew Gerrand escribió la tecnología web que lo hizo posible
    • Luego se desplegó también en el blog y en la documentación online de paquetes
  • The Go playground se ofrece como un sandbox público donde las personas pueden probar y desarrollar código libremente

La posición que alcanzó Go después de 14 años

  • Gracias a las decisiones tomadas en el diseño del lenguaje y en el proceso de desarrollo, Go se consolidó como algo más que un simple lenguaje de programación: como una forma de escribir software
  • A la posición que alcanzó Go contribuyeron estos elementos
    • Una biblioteca estándar sólida que implementa la mayoría de las funciones básicas necesarias para código de servidor
    • Concurrencia como componente de primera clase del lenguaje
    • Un enfoque basado en composición más que en herencia
    • Un modelo de empaquetado que aclara la gestión de dependencias
    • Herramientas integradas, rápidas, de compilación y pruebas
    • Formateo estricto y coherente
    • Una actitud que prioriza la legibilidad por encima del ingenio
    • Garantía de compatibilidad
  • La comunidad Go está compuesta por Gophers diversos y activos, y cumple un papel clave en el éxito del proyecto
  • Como resultado, el código Go se ve y se comporta de forma similar sin importar quién lo haya escrito, hay relativamente pocas facciones que usen subconjuntos distintos del lenguaje, y se garantiza que seguirá compilando y ejecutándose con el paso del tiempo
  • Estas características pueden considerarse un resultado poco común entre los principales lenguajes de programación

1 comentarios

 
GN⁺ 2024-01-05
Comentarios de Hacker News
  • Es realmente de agradecer que las figuras clave se tomen el tiempo para dejar una retrospectiva, y eso ayuda mucho a quienes de verdad quieren generar cambios ahora
    Aun así, no parece que Rob Pike haya explicado con suficiente claridad qué hizo bien Go. Go gestionó bien no solo el lenguaje en sí, sino también las fuerzas que actuaban sobre el proyecto: limitó su uso objetivo a la programación de sistemas, definió claramente el lenguaje y sus principios, mantuvo una actitud de priorizar la calidad arreglando problemas antes del despliegue, y controló estrictamente el lenguaje, los lanzamientos y el mensaje central, mientras dejaba espacio para que la comunidad liderara en áreas secundarias
    Un punto poco valorado es que Google en sí no intervino demasiado. Tal vez porque Go realmente contribuía a los objetivos de Google y era importante para la empresa. Queda la duda de si eso podría ocurrir hoy con proyectos nuevos
    Dart casi no se adoptó fuera de Flutter, lo cual es interesante si se compara con la realidad de que hay mucho más código de aplicaciones que código de sistemas. Go fue una tecnología clave para mover el software del lado del servidor desde el pesado ecosistema de Java hacia contenedores nativos, y se convirtió en la base de gran parte de la infraestructura de aplicaciones web de los últimos 10 años. Incluso con ese crecimiento, el equipo se mantuvo pequeño y las figuras clave siguieron ahí, aunque no está claro si eso seguirá siendo así

    • Go tiene recolección de basura y un runtime pesado que incluye green threads, así que la interoperabilidad con C se vuelve incómoda y lenta
      Por diseño, no es adecuado como lenguaje de programación de sistemas. Como al principio se describía a sí mismo de esa forma, ese mito extraño duró mucho tiempo, pero parece que esa frase fue eliminada hace algunos años
      Go está más cerca de competir con la familia de Java que con C o Rust, y eso se nota en sus áreas reales de uso. Aun así, por su sistema de tipos limitado y su excelente soporte de concurrencia, suele ubicarse un poco más abajo en el stack
    • Me cuesta estar de acuerdo con esa parte de que la comunidad lidera. Sigue dándose mucho la sensación de que Google decide
      Eso se vio especialmente en la gestión de versiones. Al principio Go ni siquiera reconocía que existiera el problema, y cuando el consenso de la comunidad creció, dejaron atrás la discusión previa y decidieron que ahora se irían por modules
      También recuerdo que no querían convertir el tiempo monótono (monotonic time) en una API pública hasta que Cloudflare sufriera una caída por el horario de verano
    • Fui desarrollador Java y ahora me gusta Go, pero no estoy seguro de que a mis antiguos colegas de Java les fuera a gustar. Go, incluso cuando era un lenguaje nuevo, era un lenguaje algo aburrido
      En el mundo Java hay diversión en refactorizar bases de código usando funciones nuevas, por ejemplo excelentes streams. En Go, la diversión viene de encontrar la abstracción más simple y más común
    • Trabajo en Dart. No me parece que la crítica de que Dart no se adopta fuera de Flutter sea tan devastadora
      Todos los lenguajes necesitan bibliotecas y frameworks adecuados para un área específica. Flutter es el framework para escribir apps cliente en Dart, y sin Flutter, por mucho que te guste el lenguaje Dart, tendrías que invertir una enorme cantidad de tiempo empezando por cómo dibujar píxeles en pantalla en Android y iOS
      Cualquiera que escriba aplicaciones construye sobre una pila de bibliotecas y frameworks. La diferencia entre Go y Dart junto con Flutter es que las bibliotecas de dominio necesarias para el servidor, como networking, serialización y criptografía, en Go vienen dentro de la biblioteca estándar
      Dart tiene elementos básicos como colecciones y asincronía en bibliotecas integradas, pero las funciones específicas de cada dominio dependen de paquetes externos como Flutter. En gran parte eso se debe a que Dart tuvo un sistema de gestión de paquetes sólido desde una etapa muy temprana. Muchas bibliotecas clave escritas y mantenidas por el equipo de Dart también se distribuyen por el gestor de paquetes en vez de venir integradas, porque así es mucho más fácil hacerlas evolucionar
      Los frameworks de UI tienden a durar menos que los lenguajes, así que me parece mejor que Flutter no esté incrustado en la biblioteca estándar de Dart. Flutter es excelente, pero no sorprendería que en 20 años exista algo mejor, y si Flutter no está integrado, a los usuarios de Dart les resultará más fácil cambiarse en ese momento
    • Durante los despidos recortaron equipos clave de FOSS, y a la gerencia realmente no le gusta el FOSS de verdad. No se ve bien en la hoja de cálculo de Ruth
      Algo como Android, que es un FOSS falso, se tolera por razones estratégicas, pero fuera de eso parece haber una actitud de que, si no lleva a clics en anuncios, no sirve de nada
  • En 2012, como desarrollador Python, tuve que hacer manipulación de bits para Modbus y ahí empecé con Go. No desplegué ese código, pero me impactó lo fácil que era tocar bits y bytes y que simplemente funcionara
    Diez años después, y tras pasar por varios trabajos casi de tiempo completo con Go, todavía me sorprende que la mayor parte simplemente funcione
    Me gustan el lenguaje Go y la comunidad de Go. Agradezco el trabajo de la gente que hace Go, como Rob, Ian y Russ, y también aprecio que esta charla y texto hayan tratado con honestidad ese “tramo difícil” con la comunidad
    Aun así, habiéndolo vivido, me resulta muy difícil creer que realmente no supieran con exactitud cómo estaban actuando, especialmente en el caos de la gestión de paquetes. De todos modos, también me parece acertada la valoración del texto de que al final llegaron a una buena solución
    Espero que los próximos 10 años de Go, y lenguajes similares inspirados por él como Zig y Deno, sigan creciendo con comunidades sanas

    • Lo que más me gusta del equipo central de Go es que supo decir “no” a todo tipo de peticiones y que con los genéricos pudo decir “esperemos a la implementación correcta”
      Soy más biólogo computacional que programador, así que mi uso de Go es irregular, pero cuando vuelvo a Go, mi código antiguo compila y el lenguaje se comporta como esperaba
      Al mismo tiempo, valoro que Rob Pike reconociera los errores cometidos en el proceso de aprender a incorporar la participación de la comunidad sin aceptar todas las funciones vistosas
    • Vengo de Python. Últimamente estoy usando herramientas de IA como Bard y GPT-4 para convertir directamente utilidades viejas de Python a Go
      Algunas funcionaron de inmediato, y otras necesitaron pequeños retoques porque el modelo de IA tenía información algo desactualizada sobre la API
      Aun así, el hecho de copiar el programa al servidor con scp y que simplemente funcione es sorprendente, comparado con el baile de Python de “a ver, ¿cuál es el sistema venv de moda esta vez?”
    • No me parece que Zig y Deno sean comparaciones tan adecuadas con Golang. Más bien Elixir y ReasonML se parecen más, y dentro de Google también está Dart
  • Es un buen artículo, pero también se siente bastante autocomplaciente. Esperaba que reconocieran problemas más profundos del lenguaje, pero parece que la idea central es que para ellos el lenguaje es perfecto y el problema soy yo
    Por eso me cuesta recomendar Go. Tiene problemas con el sistema de tipos, el manejo de errores, la concurrencia insegura, una sintaxis demasiado simplista, nil, los valores cero por defecto y muchos paquetes principales descuidados
    Ahora uso Rust como lenguaje principal. Su ecosistema es activo y tiene una visión en muchos aspectos en los que Go no la tiene
    Dicho de forma más directa, parece que Go tuvo su momento cuando competía con PHP como lenguaje de backend

    • Esto no parece aportar mucho a la discusión. Se lee más como una defensa de Rust sin puntos concretos
      El primer párrafo es “no me gusta”, el segundo es sarcasmo, el tercero también es “no me gusta”, el cuarto es “Rust es mejor” y el quinto vuelve a ser sarcasmo
    • A los científicos de la línea de Plan 9 les gusta demasiado su propia marca. Empecé a usar Go en 2012, pero lo dejé después de deshacerme de deps.dev
      Años después intenté introducir Go en el equipo en la empresa para terminar un trabajo, pero otro ingeniero se tomó el tiempo de revisar el lenguaje y enumeró las razones por las que no era tan bueno, y tenía razón
      La idea central era que, aunque es simple, cosas menores como el manejo de errores y los imports no usados hacen que usarlo sea doloroso. Personalmente me gustaba el manejo de errores, pero odiaba el sistema de tipos
    • Usé Go antes de aprender Rust, y al ver la diferencia entre ambos cambié mucho de opinión
      Como antes había usado OCaml, ya conocía el valor de los tipos Option y Result frente a try/catch o nil, pero usaba Go porque era fácil. Sin embargo, esa facilidad trae un costo de mantenimiento a largo plazo. Es mejor dejar las cosas bien desde el principio y no enfrentarse a las dificultades después
      No quiero sonar frívolo, pero muchas veces he oído describir a Go como un lenguaje que tomó los avances de los últimos 50 años en lenguajes de programación y los tiró a la basura
    • Coincido con la impresión sobre el lenguaje de programación, pero en defensa de Go, Go se parece más a un lenguaje hecho por gente a la que no le interesan los lenguajes de programación en sí, y para ese tipo de gente
      No lo digo en mal sentido. A Rob Pike no le importa que la concurrencia no sea segura o que nil sea el error de mil millones de dólares, y a la mayoría de los usuarios de Go tampoco
      Eso no significa que Go sea un buen lenguaje. Pero ese no es el punto. Go es un lenguaje conveniente y suficientemente decente, combinado con herramientas convincentes que lo hacen fácil de usar. Es como los Crocs del mundo de los lenguajes de programación
    • Aprendí Rust, pero la comunidad de usuarios de Rust hace imposible que me guste el lenguaje. No ha cambiado en años y, si acaso, ha empeorado en vez de mejorar
      Si necesitara alta integridad y seguridad, usaría Ada o Ada/SPARK con verificación formal. Fuera de eso, Go da una productividad mucho mayor que Rust
      Personalmente veo a Rust como un caso emblemático de sobreingeniería, igual que C++
  • Sonará amargo, pero hace 10 años se burlaron de mí en go-nuts y hasta Rob Pike me respondió de forma despectiva. Todo porque me atreví a decir que la forma en que funcionaban go get y las importaciones de módulos por red, promovida en la documentación para principiantes y usada así por toda la comunidad, terminaría siendo dañina y miope

    • El sistema de paquetes de Go, especialmente el de antes de modules, se siente como un hack que forzó un comportamiento externo sobre un sistema más restringido diseñado para usarse solo dentro del monorepo de Google
      Ese extraño árbol de espacio de nombres global tiene sentido ahí dentro, y el énfasis en los artefactos de código generados incluidos en el repositorio también se entiende si se considera que Google mete hasta los resultados de compilación en su monorepo
    • Aun así, es interesante que los resultados producidos por Go sean mejores que los de otros lenguajes
      Claro, están el problema de v2 y el problema del eterno v0. Aun así, es mejor que usar npm o tratar con alguna cosa de Python que te hace persignarte
    • Básicamente expuso ante el mundo lo ignorante que es Rob Pike respecto a la teoría de lenguajes de programación descubierta después del año 2000
  • Este artículo parece una versión más personal del artículo que publicaron en ACM [1]. Ambos reconocen que no crearon un gran lenguaje de programación nuevo desde la perspectiva de la especificación del lenguaje, sino que construyeron muy bien todo lo que rodea al lenguaje, y eso quizá sea más importante
    En el artículo enviado dicen que inventaron su enfoque para usar interfaces y su enfoque de concurrencia. Pero las goroutines son iguales a los threads de Haskell, y las interfaces, ahora que admiten parámetros genéricos, se parecen mucho a las typeclasses de Haskell. Dado que Haskell se adelantó a Go, resulta interesante ver a programadores procedimentales descubrir por su cuenta la fuerza de ideas de la programación funcional
    Una innovación del lenguaje Go es que una implementación de interfaz no necesita declarar la interfaz que implementa. Desde el punto de vista de la seguridad es terrible, pero en la práctica casi no causa problemas y elimina los horribles problemas de dependencias circulares que se ven en Haskell y Rust
    [1] https://cacm.acm.org/magazines/2022/5/260357-the-go-programm...

    • Parece que se subestima cuánto importa el hecho de que las interfaces sean de tipado estructural en el efecto general que esta característica tuvo sobre el lenguaje y, más allá, sobre sus modismos y ecosistema
      Si los tipos hubieran tenido que declarar por adelantado qué interfaces implementaban, Go habría sido un lenguaje completamente distinto. Ese fue uno de sus principales factores diferenciadores. Al menos hasta que TypeScript apareció con interfaces de tipado estructural por otras razones
    • El tipado estructural ya es un concepto de hace más de 25 años, y ya se usaba en lenguajes como OCaml y Scala
    • No veo cómo eso sería una innovación. Todos los lenguajes principales anteriores a Go que se me ocurren funcionan así
      ¿Puedes mencionar хотя бы un lenguaje de programación que nos importe, en el que al definir una interfaz se obligue a proporcionar una implementación de esa interfaz?
  • Parece que los problemas relacionados con criptografía de Go todavía no se han mencionado mucho. Después de que OpenSSH descartó SHA1, al equipo de Go le tomó nada menos que un año agregar soporte para SHA2 en x/crypto/ssh [1]. Gitea fue una víctima conocida [2]
    Además, ver al mantenedor de criptografía atacar a GnuPG [3] e intentar socavar la credibilidad de Dan Bernstein [4] no inspira confianza
    [1]: https://github.com/golang/go/issues/49269
    [2]: https://github.com/go-gitea/gitea/issues/17798
    [3]: https://twitter.com/FiloSottile/status/1127643698676797441
    [4]: https://twitter.com/FiloSottile/status/1555669786826244096

    • El problema con los paquetes x/ es que muchos tienden a quedar abandonados y siguen un modelo de desarrollo que dificulta que nuevos colaboradores logren enviar parches con éxito
      En cambio, considero que el paquete crypto de la biblioteca estándar es uno de los grandes aciertos de Go. OpenSSL fue un desastre durante mucho tiempo, y Go, especialmente agl, creó y distribuyó una implementación alternativa de uso muy amplio, con una calidad promedio alta. Esto es especialmente cierto desde la perspectiva de que hay muchos menos footguns en la API pública
  • Este texto menciona gofmt como algo que “se hizo bien”, y vale la pena subrayarlo especialmente
    Para muchos diseñadores y defensores de lenguajes puede parecer algo menor, pero desde el primer día del proyecto Go aporta un valor enorme al eliminar por completo una de las típicas discusiones absurdas de bike-shedding
    He visto que varios lenguajes más nuevos lo copian deliberadamente, o lo adoptan pensando: como Go lo tiene, nosotros también deberíamos hacerlo
    He visto reglas de formato bastante extrañas, pero casi no he visto reglas a las que uno no pueda acostumbrarse. En cambio, muchas veces en codebases sin reglas de formato obligatorias el resultado terminaba siendo un código más difícil de entender

    • Tengo la tendencia a estar toqueteando el formato del código, así que me gusta que go fmt vuelva eso mayormente irrelevante
  • Coincido con la idea de que “la existencia de una biblioteca sólida y bien construida, con la mayoría de lo necesario para escribir código de servidor del siglo XXI, fue una gran ventaja”.
    Go fue financiado por Google porque Google tenía un problema. Necesitaba escribir mucho código de aplicaciones del lado del servidor, Python era demasiado lento y C++ era demasiado frágil. Go encajó muy bien en ese espacio intermedio.
    Un gran extra era que la gente de Google escribía las bibliotecas necesarias para ese tipo de trabajo y también las usaba internamente. Así que, al usar esas bibliotecas, incluso los caminos de error eran código muy recorrido en la práctica.
    Tengo desacuerdos técnicos con el lenguaje. En particular, el énfasis inicial en usar colas para todo, incluso en construir mutexes con colas. Pero sí acertaron con los green threads adecuados para el caso de uso. El problema del color de las funciones es real, y los trucos enrevesados de Rust para obligarte a usar asincronía y threads juntos son demasiado. Go sacrifica un poco de rendimiento y obtiene una gran simplicidad a cambio.
    También resulta interesante la vieja aversión a los threads. Empecé con mainframes UNIVAC, que ya tenían threads desde 1967, aunque entonces se llamaban “activities”. En 1972 no solo había threads en espacio de usuario, también corrían sobre multiprocesadores simétricos. Ya existían E/S asíncrona y funciones de finalización en espacio de usuario, instrucciones integradas para locks y sistemas operativos internamente basados en threads.
    Los threads y los multiprocesadores se consideraban algo normal, y al pasar a UNIX sentí que habíamos perdido todo eso. A UNIX/Linux le tomó décadas ponerse al día en esta área, y varias generaciones de programadores no supieron usar multiprocesadores de memoria compartida.
    La teoría temprana de concurrencia de Dijkstra se reinventó con frecuencia con otra terminología y, a menudo, con peores diseños. La gente de Go entendía el bounded buffer de Dijkstra y por qué los bounded buffers de longitud 0 y 1 son útiles. Fue bueno volver a ver eso. Con las primitivas correctas, la concurrencia no es tan difícil. Si intentas resolverlo esparciendo locks por todos lados, al estilo clásico de pthreads, no funciona bien. Tampoco ayudó que en los primeros tiempos de UNIX/Linux el scheduler de CPU fuera realmente pésimo, así que desbloquear threads funcionaba horriblemente.

    • Los comentaristas aleatorios de HN suelen decir cosas como “Go fue financiado porque Google tenía un problema”, pero eso pasa porque no entienden bien cómo funciona Google ni lo grande que es la empresa.
      Go se creó porque Rob Pike tenía un problema. No le gustaban C++, Java ni Python y, como era Rob Pike, Google le permitió pasarse años trabajando en un lenguaje nuevo. No fue que Larry, Sergey, Sundar o Eric fueran a buscar a Rob para pedirle que hiciera algo.
      En términos generales, Go fue financiado porque Rob Pike pidió apoyo, Google quería retener a Rob Pike y asumía que casi con seguridad haría algo interesante. En Google Engineering, el dinero de la empresa en la práctica se parece más a permitir que la gente pueda dedicar tiempo a trabajar en algo.
      Como Rob Pike era un ingeniero brillante, Go terminó saliendo bastante bien en varios aspectos y encajaba con el problema inicial que él tenía al tener que usar C++, Python y Java para escribir aplicaciones operativas en Google. Algunas personas dentro de Google también estuvieron de acuerdo y empezaron a usar Go, y entre ellas hubo gente influyente que “animó” a otros a usar Go en lugar de Python.
    • Go no fue simplemente un proyecto de Google, sino básicamente la filosofía de compiladores de Plan 9 y CSP para Unix/Linux.
    • Esto no coincide con mi recuerdo de haber trabajado en Google en esa época. Igual que el meme de que “Go era para programación de sistemas”, esto se parece más a una creencia que, al examinarla, no tiene mucho sentido.
      En Google en 2009, salvo por el caso de la adquisición de YouTube, Python casi no se usaba en servidores más allá de unas pocas utilidades internas, así que esa idea se puede descartar de inmediato. El servidor más grande en Python hecho directamente por Google era Mondrian, la herramienta de revisión de código escrita por Guido van Rossum, y luego fue reemplazada por Critique, escrita en Java.
      En esa época Google tenía una política bastante estricta de tres lenguajes para evitar guerras de lenguajes de programación. Se podían usar C++, Java y Python; Python se usaba para scripts, C++ para infraestructura y Java para servidores web.
      Había superposición. Java se adoptó después de C++, y algunos servidores web siguieron en C++ porque era difícil reescribirlos. También hubo intentos de migración gradual a Java, pero no terminaron de consolidarse. Algunos servidores de infraestructura, como Megastore, estaban escritos en Java, y las bibliotecas centrales solían estar en C++ con JNI y bindings para Python.
      También había huecos en la política, así que algunos equipos crearon sus propios lenguajes para infraestructura interna, y en la base de código real también se usaban lenguajes de configuración personalizados y Sawzall, creado por Rob Pike. La sintaxis se parecía un poco a la de Go.
      En general, la gente tenía bastante libertad de elección, y los nuevos servidores web se escribían principalmente en Java, mientras que cosas como nuevos motores de base de datos se hacían sobre todo en C++.
      Es cierto que escribir código asíncrono era claramente difícil, y Go sí resolvió bien esa parte. Pero Go no fue un proyecto iniciado por la alta dirección para resolver los problemas de Google. No porque la dirección temiera ese tipo de proyectos, sino porque ya había muchísimos proyectos internos de infraestructura creados por los equipos correspondientes y con personal asignado específicamente para resolver dolores reales de los desarrolladores. Un ejemplo eran las grandes actualizaciones de red que se impulsaron en esa época. Ese tipo de proyectos se anunciaban con anticipación.
      No recuerdo que hubiera grandes quejas sobre ese estado de cosas. Los tiempos de build sí eran un problema hasta que Bazel/Blaze empezó a funcionar con clústeres de build remoto; después de eso, como todo quedaba cacheado en remoto, incluso bases de código enormes podían compilarse en segundos. La velocidad del compilador local dejó de importar casi por completo, y javac también era muy rápido.
      El anuncio de Go fue bastante sorpresivo. Si de verdad se hubiera desarrollado para resolver los problemas de Google, lo normal dentro de Google habría sido lanzarlo primero internamente, desarrollarlo durante años con usuarios internos y luego publicarlo. Pero con Go pasó al revés: primero salió al público. La gente a mi alrededor se preguntaba para qué era esto, una reacción poco esperable si realmente hubiera estado impulsado por demanda interna.
  • Resulta interesante la parte sobre la decisión de usar el compilador de C de Ken Thompson en lugar de LLVM. La gente se quejó y, especialmente en las primeras versiones, eso también llevó a que el código generado estuviera menos optimizado.
    A cambio, pudieron implementar pilas segmentadas rápidamente. Si hubieran tenido que implementarlo en LLVM y ajustarlo al ABI de LLVM, quizá ni siquiera habrían podido hacerlo.
    El texto no lo presenta como el único beneficio de esa decisión, sino como un ejemplo de sus ventajas.

    • Lo relacionado con LLVM en la entrevista no es exacto. Se implementaron pilas segmentadas en LLVM para Rust y, en realidad, fue bastante fácil.
      Ya había soporte en X86FrameLowering, y existía incluso cuando se lanzó Go. Si activas el stack splitting, LLVM emite una verificación en el prólogo de la función y, si hace falta, llama a __morestack para asignar más pila. El ABI de Windows MSVC también necesita un código muy parecido para soportar _chkstk, así que encaja de forma natural con el soporte para __morestack.
      De hecho, lograr que GDB entendiera las pilas segmentadas fue más difícil que cualquier parte de la implementación del compilador. Eso no depende del backend.
      Probablemente el autor lo confundió con las pilas reubicables. En ese momento eran difíciles de implementar porque requerían recolección de basura precisa, pero ahora Azul ya lo implementó en LLVM. En aquel entonces, la forma más sencilla de implementar recolección de basura precisa era hacer spill de todos los registros a través de las llamadas a funciones, y eso requería más esfuerzo de implementación, pero no era algo desmedido. Como parece que el compilador de Plan 9 de todos modos hacía eso, tampoco habría sido una regresión de rendimiento frente a 6g/8g. De cualquier forma, el soporte de recolección de basura de Azul ahora ya tiene una implementación correcta que puede guardar raíces en registros.
    • ¿Al final Go no dejó atrás las pilas segmentadas? Fue en Go 1.3, lanzado en 2014. Por el problema del “hot spot” [1].
      Así que sí era valiosa la capacidad de experimentar, pero este ejemplo en particular no es un caso perfecto.
      [1]: https://go.dev/doc/go1.3#:~:text=Go%201.3%20has%20changed%20...
  • Mientras diseño un lenguaje hobby con tipado estático, estoy intentando algo que combine interfaces y genéricos, y he comprobado por mí mismo que Rob tiene razón. Hacer que ambas cosas encajen bien es realmente difícil.
    Aun así, me parece que vale la pena. Personalmente, no me resultaría muy satisfactorio usar un lenguaje de tipado estático en el que el usuario no pueda definir por sí mismo tipos genéricos.
    Hace tiempo programé en un entorno BASIC donde existía GOSUB para subrutinas, pero no había manera de escribir subrutinas que recibieran parámetros. No quisiera repetir esa experiencia al nivel del sistema de tipos.
    Por eso entiendo que al equipo de Go le haya tomado tanto tiempo encontrar un buen diseño. Diseñar un buen lenguaje es difícil; diseñar un buen lenguaje con sistema de tipos es 10 veces más difícil; y diseñar un sistema de tipos con genéricos es otras 10 veces más difícil todavía.

    • No estoy de acuerdo. La parte más difícil de implementar genéricos es cuando se soporta herencia de implementación, y Go no soporta eso.
      En realidad, Go asumió la parte más fácil de implementar en genéricos. La razón por la que no lo hicieron antes no fue técnica. Fue ideológica y basada puramente en la ignorancia, porque la mayoría de los diseñadores de Go dejaron de seguir el campo de la teoría de lenguajes de programación después de finales de los 90.
    • Los lenguajes de la familia ML y los lenguajes funcionales parecen no tener este problema, y muchos de ellos tienen sistemas de tipos mucho más sofisticados y potentes que Go.
    • Scala salió antes que Golang y ya estaba ganando popularidad cuando apareció Golang. Y no tenía detrás recursos del tamaño de GOOG.