El artículo está escrito como respuesta a un artículo antípoda publicado anteriormente.

En los últimos dos años, he estado usando Go para implementar un servidor RADIUS especializado con un sistema de facturación desarrollado. En el proceso, aprendo las sutilezas del lenguaje mismo. Los programas en sí mismos son muy simples y no tienen el propósito de escribir un artículo, pero la experiencia de usar Go en sí merece decir algunas palabras en su defensa. Go se está convirtiendo en un lenguaje cada vez más popular para códigos serios y escalables. El lenguaje se crea en Google, en el que se usa activamente. Para resumir, creo sinceramente que el diseño del lenguaje Go es malo para los programadores tontos.
Diseñado para programadores débiles?
Hablar débil sobre problemas. Charla contundente sobre ideas y sueños ...
Go es muy sencillo de aprender, tan simple que puede leer el código con poca o ninguna preparación. Esta característica del lenguaje se usa en muchas empresas mundiales cuando el código se lee junto con especialistas no especializados (gerentes, clientes, etc.). Esto es muy conveniente para metodologías como Design Driven Development.
Incluso los programadores novatos comienzan a producir un código bastante decente después de una o dos semanas. El libro que estudié Go se llama "Programación Go" (escrito por Mark Summerfield). El libro es muy bueno, toca muchos de los matices del lenguaje. Después de lenguajes innecesariamente complicados como Java, PHP, la falta de magia es refrescante. Pero tarde o temprano, muchos programadores limitados desean utilizar métodos antiguos en un campo nuevo. ¿Es esto realmente necesario?
Rob Pike (el principal ideólogo del lenguaje) creó Go como un lenguaje industrial que es fácil de leer, efectivo para usar. El lenguaje está diseñado para la máxima productividad en equipos grandes y no hay duda al respecto. Muchos programadores novatos se quejan de que hay muchas características que les faltan. Este deseo de simplicidad fue una decisión consciente de los desarrolladores de lenguaje, y para comprender completamente para qué era, necesitamos entender la motivación de los desarrolladores y lo que lograron en Go.
Entonces, ¿por qué se hizo tan simple? Aquí hay un par de citas de Rob Pike:
El punto clave aquí es que nuestros programadores no son investigadores. Por lo general, son muy jóvenes, vienen a nosotros después de la escuela, tal vez estudiaron Java, C / C ++ o Python. No pueden entender un lenguaje sobresaliente, pero al mismo tiempo, queremos que creen un buen software. Es por eso que el lenguaje debe ser fácil de entender y aprender.
Debería ser familiar, más o menos hablando como C. Los programadores de Google comienzan sus carreras temprano y están familiarizados con los lenguajes de procedimiento, en particular con la familia C. El requisito de productividad rápida en un nuevo lenguaje de programación significa que el lenguaje no debe ser demasiado radical.
Palabras sabias, ¿verdad?
Artefactos de simplicidad
La simplicidad es una condición necesaria para lo bello. Leo Tolstoi.
Ser simple es una de las aspiraciones más importantes en cualquier diseño. Como saben, un proyecto perfecto no es un proyecto en el que no hay nada que agregar, sino uno, del cual no hay nada que eliminar. Muchos creen que para resolver (o incluso expresar) tareas complejas, se necesita una herramienta compleja. Sin embargo, esto no es así. Tomemos, por ejemplo, el lenguaje PERL. Los ideólogos del lenguaje creían que un programador debería tener al menos tres formas diferentes de resolver un problema. Los ideólogos del lenguaje Go tomaron un camino diferente, decidieron que para lograr el objetivo, un camino es suficiente, pero realmente bueno. Este enfoque tiene una base seria: la única forma es más fácil de aprender y más difícil de olvidar.
Muchos migrantes se quejan de que el lenguaje no contiene abstracciones elegantes. Sí, lo es, pero esta es una de las principales ventajas del lenguaje. El lenguaje contiene un mínimo de magia, por lo tanto, no se requieren conocimientos profundos para leer el programa. En cuanto a la verbosidad del código, esto no es un problema en absoluto. Un programa Golang bien escrito se lee verticalmente con poca o ninguna estructuración. Además, la velocidad de lectura de un programa es al menos un orden de magnitud más rápido que su escritura. Si considera que todo el código tiene un formato uniforme (realizado con el comando gofmt incorporado), leer algunas líneas adicionales no es un problema en absoluto.
Poco expresivo
El arte no tolera cuando restringe su libertad. La precisión no es su responsabilidad.
Debido al deseo de simplicidad, Go carece de construcciones que en otros idiomas se perciben como algo natural para las personas acostumbradas a ellos. Al principio, esto puede ser algo inconveniente, pero luego se da cuenta de que el programa se lee muchas veces más fácil y más definido.
Por ejemplo, una utilidad de consola que lee stdin o un archivo de los argumentos de la línea de comandos se verá así:
package main import ( "bufio" "flag" "fmt" "log" "os" ) func main() { flag.Parse() scanner := newScanner(flag.Args()) var text string for scanner.Scan() { text += scanner.Text() } if err := scanner.Err(); err != nil { log.Fatal(err) } fmt.Println(text) } func newScanner(flags []string) *bufio.Scanner { if len(flags) == 0 { return bufio.NewScanner(os.Stdin) } file, err := os.Open(flags[0]) if err != nil { log.Fatal(err) } return bufio.NewScanner(file) }
Aunque la solución al mismo problema en el lenguaje D parece un poco más corta, sin embargo, no es más fácil de leer
import std.stdio, std.array, std.conv; void main(string[] args) { try { auto source = args.length > 1 ? File(args[1], "r") : stdin; auto text = source.byLine.join.to!(string); writeln(text); } catch (Exception ex) { writeln(ex.msg); } }
Copia el infierno
El hombre lleva el infierno en sí mismo. Martin Luther
Los principiantes se quejan constantemente de Go en términos de falta de genéricos. Para resolver este problema, la mayoría de ellos utilizan la copia directa de código. Por ejemplo, estos profesionales desafortunados creen que la función de resumir la lista de enteros no se puede implementar de otra manera que simplemente copiar y pegar para cada tipo de datos.
package main import "fmt" func int64Sum(list []int64) (uint64) { var result int64 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func int32Sum(list []int32) (uint64) { var result int32 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(int32Sum(list32)) fmt.Println(int64Sum(list64)) }
El lenguaje tiene medios suficientes para implementar tales construcciones. Por ejemplo, la programación generalizada está bien.
package main import "fmt" func Eval32(list []int32, fn func(a, b int32)int32) int32 { var res int32 for _, val := range list { res = fn(res, val) } return res } func int32Add(a, b int32) int32 { return a + b } func int32Sub(a, b int32) int32 { return a - b } func Eval64(list []int64, fn func(a, b int64)int64) int64 { var res int64 for _, val := range list { res = fn(res, val) } return res } func int64Add(a, b int64) int64 { return a + b } func int64Sub(a, b int64) int64 { return a - b } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(Eval32(list32, int32Add)) fmt.Println(Eval64(list64, int64Add)) fmt.Println(Eval64(list64, int64Sub)) }
Y, aunque nuestro código resultó ser algo más largo que el caso anterior, se generalizó. Por lo tanto, no será difícil para nosotros implementar todas las operaciones aritméticas.
Muchos dirán que el programa D parece significativamente más corto y tendrá razón.
import std.stdio; import std.algorithm; void main(string[] args) { [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln; }
Sin embargo, solo es más corto, pero no más correcto, ya que el problema de manejo de errores se ignora por completo en la implementación de D.
En la vida real, cuando la complejidad de la lógica aumenta, la brecha se está reduciendo rápidamente. Aún más rápidamente, la brecha se reduce cuando se requiere una acción que no se puede realizar utilizando operadores de lenguaje estándar.
En términos de compatibilidad, extensibilidad, legibilidad, en mi opinión, el lenguaje Go gana, aunque pierde en verbosidad.
La programación generalizada en algunos casos nos brinda beneficios innegables. Esto ilustra claramente el paquete de clasificación. Entonces, para ordenar cualquier lista, es suficiente para nosotros implementar la interfaz sort.Interface.
import "sort" type Names []string func (ns Names) Len() int { return len(ns) } func (ns Names) Less(i, j int) bool { return ns[i] < ns[j] } func (ns Names) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } func main() { names := Names{"London", "Berlin", "Rim"} sort.Sort(names) }
Si toma algún proyecto de código abierto y ejecuta el comando grep "interface {}" -R, verá con qué frecuencia se usan las interfaces confusas. Los camaradas de mente estrecha dirán de inmediato que todo esto se debe a la falta de genéricos. Sin embargo, esto está lejos de ser siempre el caso. Tomemos, por ejemplo, el lenguaje DELPHI. A pesar de tener estos mismos genéricos, contiene un tipo especial VARIANTE para operaciones con tipos de datos arbitrarios. Ir hace lo mismo.
De un arma en gorriones
Y una camisa de fuerza debe ajustarse al tamaño de la locura. Stanislav Lets.
Muchos fanáticos de los deportes extremos pueden decir que Go tiene otro mecanismo para crear genéricos: la reflexión. Y tendrán razón ... pero solo en casos raros.
Rob Pike nos advierte:
Esta es una herramienta poderosa que debe usarse con cuidado. Debe evitarse hasta que sea estrictamente necesario.
Wikipedia nos dice lo siguiente:
Reflexión significa un proceso durante el cual un programa puede rastrear y modificar su propia estructura y comportamiento en tiempo de ejecución. El paradigma de programación subyacente a la reflexión se llama programación reflexiva. Este es un tipo de metaprogramación.
Sin embargo, como sabes, tienes que pagar por todo. En este caso, es:
- dificultad para escribir programas
- velocidad de ejecución del programa
Por lo tanto, la reflexión debe usarse con precaución, como un arma de gran calibre. El uso irreflexivo de la reflexión conduce a programas ilegibles, errores constantes y baja velocidad. Lo único es que el programador snob podría hacer alarde de su código frente a otros colegas más pragmáticos y modestos.
Equipaje cultural de C? No, de varios idiomas!
Junto con el estado, los herederos quedan con deudas.
A pesar del hecho de que muchos creen que el lenguaje está completamente basado en el legado de C, esto no es así. El lenguaje ha incorporado muchos aspectos de los mejores lenguajes de programación.
Sintaxis
En primer lugar, la sintaxis de las construcciones gramaticales se basa en la sintaxis del lenguaje C. Sin embargo, el lenguaje DELPHI también tuvo un impacto significativo. Entonces, vemos que el exceso de paréntesis se elimina por completo, lo que reduce en gran medida la legibilidad del programa. Además, el lenguaje contiene el operador :: =, que es inherente al lenguaje DELPHI. El concepto de paquetes se toma prestado de idiomas como ADA. La declaración de entidades no utilizadas se toma prestada del lenguaje PROLOG.
Semántica
La semántica del lenguaje DELPHI se tomó como base de los paquetes. Cada paquete encapsula datos y código y contiene entidades privadas y públicas. Esto le permite reducir la interfaz del paquete al mínimo.
La operación de implementación por método de delegación fue prestada de DELPHI.
Compilación
No es de extrañar que haya una broma: Go se desarrolló mientras se compilaba el programa C. Una de las fortalezas del lenguaje es la compilación ultra rápida. La idea fue tomada de DELPHI. Además, cada paquete Go corresponde al módulo DELPHI. Estos paquetes se vuelven a compilar solo cuando es necesario. Por lo tanto, después de la próxima edición, no es necesario compilar todo el programa, pero es suficiente recompilar solo los paquetes y paquetes modificados dependiendo de estos paquetes modificados (y eso solo si las interfaces del paquete han cambiado).
Diseños de alto nivel
El lenguaje contiene muchas construcciones diferentes de alto nivel que de ninguna manera están asociadas con lenguajes de bajo nivel como C.
- Líneas
- Tabla hash
- Rebanadas
- La escritura de pato se toma prestada de idiomas como RUBY (que, desafortunadamente, muchos no entienden y no utilizan en todo su potencial).
Gestión de la memoria
La gestión de la memoria generalmente merece un artículo separado. Si en lenguajes como C ++, el control se deja completamente al desarrollador, entonces, en lenguajes posteriores como DELPHI, se utilizó un modelo de conteo de referencia. Con este enfoque, no se permitieron enlaces cíclicos, ya que se formaron grupos perdidos, entonces la detección de dichos grupos (como en C #) está integrada en Go. Además, el recolector de basura es más efectivo que la mayoría de las implementaciones conocidas actualmente y ya se puede usar para muchas tareas en tiempo real. El lenguaje en sí mismo reconoce situaciones en las que se puede asignar un valor para almacenar una variable en la pila. Esto reduce la carga en el administrador de memoria y aumenta la velocidad del programa.
Concurrencia y competencia
El paralelismo y la competitividad del lenguaje están más allá de los elogios. Ningún lenguaje de bajo nivel puede competir remotamente con el idioma Go. Para ser justos, vale la pena señalar que el modelo no fue inventado por los autores del lenguaje, sino simplemente tomado del antiguo lenguaje ADA. El lenguaje es capaz de procesar millones de conexiones paralelas utilizando todas las CPU, mientras que en el orden de magnitud es menos común para problemas complejos de código multiproceso con puntos muertos y condiciones de carrera.
Beneficios adicionales
Si esto es beneficioso, todos se volverán desinteresados.
El lenguaje también nos brinda una serie de beneficios indudables:
- El único archivo ejecutable después de compilar el proyecto simplifica enormemente las aplicaciones de implementación.
- La escritura estática y la inferencia de tipos pueden reducir significativamente la cantidad de errores en el código incluso sin escribir pruebas. Conozco algunos programadores que lo hacen sin escribir pruebas y, al mismo tiempo, la calidad de su código no se ve afectada significativamente.
- Compilación cruzada muy simple y excelente portabilidad de la biblioteca estándar, lo que simplifica enormemente el desarrollo de aplicaciones multiplataforma.
- Las expresiones regulares RE2 son seguras para subprocesos y tienen tiempos de ejecución predecibles.
- Una biblioteca estándar potente, que permite que la mayoría de los proyectos se ejecuten sin marcos de terceros.
- El lenguaje es lo suficientemente poderoso como para concentrarse en el problema, y no en los métodos para resolverlo, y al mismo tiempo lo suficientemente bajo como para que el problema pueda resolverse de manera eficiente.
- El sistema ecológico Go ya contiene herramientas listas para usar en todas las ocasiones: pruebas, documentación, gestión de paquetes, linters potentes, generación de código, un detector de condiciones de carrera, etc.
- Go versión 1.11 ahora tiene una gestión de dependencia semántica incorporada construida sobre los hosts VCS populares. Todas las herramientas que componen el ecosistema Go usan estos servicios para descargar, compilar e instalar el código de ellos de una sola vez. Y eso es genial. Con el advenimiento de la versión 1.11, el problema con las versiones del paquete también se resolvió por completo.
- Como la idea principal del lenguaje es reducir la magia, el lenguaje alienta a los desarrolladores a manejar explícitamente el manejo de errores. Y esto es correcto, porque de lo contrario, simplemente se olvidará por completo del manejo de errores. Otra cosa es que la mayoría de los desarrolladores ignoran deliberadamente el manejo de errores, prefiriendo simplemente reenviar el error en lugar de procesarlos.
- El lenguaje no implementa la metodología clásica de OOP, ya que no hay virtualidad en su forma pura en Go. Sin embargo, esto no es un problema cuando se usan interfaces. La ausencia de OOP reduce significativamente la barrera de entrada para principiantes.
Facilidad para los beneficios comunitarios
Complicar es simple, simplificar es difícil.
Go fue diseñado para ser simple y sobresalió en eso. Fue escrito para programadores inteligentes que entienden todas las virtudes del trabajo en equipo y están cansados de la infinita variabilidad de los lenguajes de nivel empresarial. Con un conjunto relativamente pequeño de construcciones sintácticas en su arsenal, prácticamente no está sujeto a cambios a lo largo del tiempo, por lo tanto, los desarrolladores han liberado mucho tiempo específicamente para el desarrollo, y no para un estudio interminable de innovaciones lingüísticas.
Las empresas también obtienen una serie de ventajas: un umbral de entrada bajo le permite encontrar rápidamente un especialista, y la inmutabilidad del idioma le permite usar el mismo código después de 10 años.
Conclusión
El gran tamaño del cerebro aún no ha convertido a un solo elefante en un premio Nobel.
Para aquellos programadores cuyo ego personal prevalece sobre el espíritu de equipo, así como los teóricos que aman las tareas académicas y la infinita "superación personal", el lenguaje es realmente malo, porque es un lenguaje artesanal de propósito general que no permite recibir placer estético del resultado de su trabajo y mostrarse un profesional frente a colegas (siempre que medimos la mente precisamente con estos criterios, y no con el coeficiente intelectual). Como todo en la vida, es una cuestión de prioridades personales. Como todas las innovaciones que valen la pena, el lenguaje ya ha recorrido un largo camino desde la negación universal hasta el reconocimiento masivo. El lenguaje es ingenioso en su simplicidad, pero, como sabes, ¡todo lo ingenioso es simple!
Resumen
Entre todas las duras críticas dirigidas a Go, destacan las siguientes declaraciones:
- Sin genéricos. Si observamos las estadísticas de los idiomas más populares, notamos que la mitad de los diez idiomas principales no tienen genéricos. Principalmente, los genéricos solo se necesitan en contenedores. Por lo tanto, la ganancia de ellos no es demasiado grande.
- Otros idiomas como Rust son mucho mejores (al menos en las categorías de sitios XXX). Nuevamente, si miramos las estadísticas de los idiomas más populares, entonces no encontraremos Rust en la lista, o estará en algún lugar debajo de la calificación. Personalmente, me gusta Rust, pero elegí Go.
- XXX tiene un bollo así. Esta es la otra cara de la moneda de la simplicidad. Si es una desventaja o no, es decisión de todos. Sin embargo, los desarrolladores del proyecto dieron su preferencia a favor de la simplicidad.
- Lanzarán Go 2.0, luego ya veremos. Esta posición la toman los observadores, no los practicantes.
- No lo suficientemente expresivo. Estoy de acuerdo, en algunas áreas la expresividad es poco convincente, pero en general es un lenguaje simple y consistente. Además, debido a la pobreza del lenguaje, nos vemos obligados a prestar más atención a la arquitectura de la aplicación desarrollada, lo que afecta positivamente su flexibilidad.
En realidad, el artículo no pensó en las ventajas sintácticas del lenguaje Go, sino más bien como una breve descripción de sus ventajas para el trabajo en equipo y la evolución efectiva del proyecto que se está desarrollando. Se entendió que el artículo habría continuado, en relación con problemas más específicos. Sin embargo, debido a la falta de interés en el tema, lo más probable es que no haya continuación.
Un experimento
No creas las palabras, ni las tuyas ni las de los extraños, pero cree las obras, tanto las tuyas como las de los extraños.
La parte final está destinada exclusivamente a esa categoría de personas que se consideran optimistas con una mentalidad constructiva y que pueden confirmar esto con sus propios asuntos. El resto de la audiencia, omita esta parte.
Este experimento fue inspirado por amigos que afirmaron que todos los optimistas con mentalidad constructiva habían dejado (al menos virtualmente) las extensiones de nuestro país y se establecieron, por ejemplo, en Stack Overflow, y aquí permanecieron en su mayoría snobs. Durante mucho tiempo no les creí, así que decidí realizar este experimento.
Se publicaron varios artículos en el centro, como resultado del análisis de los comentarios que cito.
- De hecho, se confirmó la hipótesis de mis amigos, sin embargo, todavía se encuentran personas adecuadas entre los vendedores ambulantes, aunque su porcentaje está disminuyendo rápidamente. Yuri Bykov llama a esas personas " tontos " sobre quienes descansa todo el país. Según él, su porcentaje es pequeño (alrededor del 2%). No soy tan pesimista y creo que hay muchos más.
- La ley de los medios. La información destructiva es de mucho mayor interés que la información constructiva.
- La psicología de la multitud. Esto es algo terrible, incluso convierte a una oveja abusiva en una persona adecuada. Un hombre en una multitud ya no es un hombre. No se puede hablar de objetividad. Sin argumentos lógicos, sin fuentes autorizadas o precedentes ya no le afectan.
- Responsabilidad e impunidad. Las personas están dispuestas a humillar a otros para exaltarse a sí mismos (al menos a sus propios ojos). Especialmente si no tiene que responder por ello (lo que podría ser más fácil: hizo clic en el signo menos y ni siquiera necesita escribir un comentario). Hay tanto en común entre palabras y hechos como entre un canal y una alcantarilla.
- Vanidad La mayoría de los esnobs están listos para destacarse de cualquier manera. No le temen a las barreras morales.
- Pesimismo A diferencia de los países occidentales (y especialmente de América), los sentimientos pesimistas prevalecen en el país. Como saben, un optimista busca oportunidades en medio de dificultades, y un pesimista busca dificultades entre las oportunidades. En nuestro país, casi nadie presta atención a las cualidades positivas de nada.
- La profesionalidad y la cosmovisión. La mayoría de las personas eligen las herramientas como un fin en sí mismas, y no como un medio para un fin. La gente ha olvidado cómo trabajar con la información. La gente no ve bosques detrás de los árboles. A partir de una variedad de información, no pueden extraer los pensamientos principales. Nadie quiere mirar desde un punto de vista diferente, no estándar para ellos mismos. La disidencia se suprime. No se acepta aquí.
- Amabilidad y respeto. Los grupos amistosos alabados existen solo en palabras. Los valores de desarrollo ágil son solo en papel.
- Hipocresía Puede escribir un artículo separado sobre esto en general.
- Principio Hay personas que hacen la pregunta correcta: “ ¿Qué demonios estoy haciendo? Sin embargo, no todos entienden que, debido a la falta de principios, para nosotros, el interés egoísta momentáneo es más importante que todos nuestros principios combinados. Es más fácil culpar de todo a las circunstancias y decir que nada depende de nosotros.
Con profundo respeto y simpatía por todos los optimistas de mentalidad constructiva.
Adverax