Go: propuesta: Ir 2: agregar un operador condicional ternario

Creado en 18 jul. 2019  ·  78Comentarios  ·  Fuente: golang/go

No estoy de acuerdo con la convención de Go y los argumentos de los diseñadores del lenguaje aquí https://golang.org/doc/faq#Does_Go_have_a_ternary_form y creo que es una característica real que falta en el lenguaje.

Considere en C el siguiente código:

printf("my friend%s", (nbFriends>1?"s":""));

o en C++:

std::cout << "my friend" << (nbFriends>1?"s":"") << std::endl;

En Go, provoca grandes repeticiones que pueden causar errores, o un código muy detallado e ineficiente, o ambos, para algo que debería ser sencillo:

Solución 1:

// horribly repetitive, risk of divergence between the two strings
if nbFriends > 1 { 
  fmt.Printf("my friends\n") 
} else { 
  fmt.Printf("my friend\n")
}

Solución 2:

// difficult to read
fmt.Printf("my friend")
if nbFriends > 1 { fmt.Printf("s") }
fmt.Printf("\n")

Solución 3:

// difficult to read
var plural = ""
if nbFriends > 1 { plural = "s" }
fmt.Printf("my friend%s\n", plural)

Solución 4:

// dangerous (ifTrue and ifFalse are both evaluated, 
// contrary to a real ternary operator), 
// and not generic at all (works only for strings)
func ifStr(condition bool, ifTrue string, ifFalse string) string {
  if condition { 
    return ifTrue
  }
  return ifFalse
}
fmt.Printf("my friend%s\n", ifStr(nbFriends > 1, "s", ""))

Solución 5:

// horrible to read, probably inefficient
fmt.Printf("my friend%s\n",
        func(condition bool) string {
            if condition {
                return "s"
            }
            return ""
        }(nbFriends > 1))
Go2 LanguageChange Proposal Proposal-FinalCommentPeriod

Comentario más útil

Creo que el ejemplo es algo malo teniendo en cuenta que solo se trata de un carácter y, en mi opinión, no es realmente legible (?:"s":"")
pero estoy de acuerdo en que se debe agregar el operador ternario, es una obviedad para mí
e inventé algunos ejemplos que serían útiles para mí personalmente y creo que puedes relacionarte un poco con algunos de ellos.

const PORT = production ? 80 : 8080
en lugar de

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

fmt.Printf("Running %s build!", production ? "Production" : "Debug") .. etc

Pero, por supuesto, el operador ternario es muy difícil de aprender.

Todos 78 comentarios

@gopherbot , agregue la etiqueta Go2, LanguageChange

Ver también #31659 y #32860.

Esta decisión ya ha sido consagrada en un FAQ, como usted nota. Si desea argumentar que la respuesta de las preguntas frecuentes debe cambiarse, necesita más de unos pocos ejemplos. Les prometo que ya hemos visto y considerado esos ejemplos. Lo que necesita son datos: programas reales con código que sería más simple y fácil de leer al agregar un operador condicional. También necesita argumentos contra las preocupaciones comunes sobre el operador condicional, como que hace que el código sea más difícil de leer, especialmente cuando está anidado.

Además, un punto menor, pero su ejemplo no es excelente, ya que solo funciona en inglés y no admite la localización de cadenas de mensajes.

@ianlancetaylor

En #31659 hiciste lo que pensé que era una muy buena contra-sugerencia de tener una función cond incorporada para proporcionar funcionalidad ternaria. Esto tenía que ser una función integrada (a diferencia de una función genérica) para permitir la evaluación de cortocircuito de los argumentos verdadero/falso. Todavía sufría la posibilidad de que las personas pudieran anidar funciones cond , aunque personalmente no lo consideré un problema fatal porque, incluso si lo hicieran, debería ser más legible que los jeroglíficos del propio operador ternario de C. .

Como esa propuesta ya se ha cerrado, ¿tiene la intención de continuar con esa sugerencia o ha renunciado a la idea de tener una forma ternaria alternativa por completo?

Personalmente, no tengo la intención de impulsar esa idea más allá. Eso fue más un pensamiento de discusión que una sugerencia seria. Por supuesto, no me importa si alguien quiere pulirlo para convertirlo en una propuesta real. Pero para ser aceptado, creo que aún necesitaríamos ver algunos datos sobre cuánto simplificaría los programas reales existentes.

Vale, gracias por aclarar.

Uno solo tiene que mirar el código en otros lenguajes de la familia C para ver qué tan común es el operador ternario, pero sería difícil analizar el código Go en sí mismo ya que, como señaló @Phrounz en su publicación inicial, se usan varias construcciones para trabajar. en torno a su ausencia.

Usando la idea cond , su ejemplo sería:

fmt.Printf("my friend%s\n", cond(nbFriends > 1, "s", ""))

Habiendo dicho todo eso, si obtenemos genéricos, personalmente me contentaría con escribir mi propia función cond y, dada la falta de cortocircuito, solo la usaría donde los argumentos fueran baratos de evaluar.

En mi opinión, escribir más código (solo unas pocas líneas) es mejor que averiguar la regla de x ? a : b . La declaración if puede parecer detallada (no estoy seguro) pero fácil de entender.

Además, se puede abusar fácilmente de un operador condicional ternario cuando las personas escriben varios x ? a : b anidados. El beneficio de presentarlo no es lo suficientemente grande.

Veo que los operadores ternarios solo son fáciles de visualizar en funciones de una sola línea. Incluso entonces, la mayoría de las veces se ocupan del manejo de errores, en cuyo caso es mejor adherirse a un enfoque de "cuanta menos sangría, mejor" haciendo que el error o la ruta menos probable para una función se envuelva en un si y se maneje entonces, en lugar de tener una lógica de bifurcación múltiple.

Creo que el ejemplo es algo malo teniendo en cuenta que solo se trata de un carácter y, en mi opinión, no es realmente legible (?:"s":"")
pero estoy de acuerdo en que se debe agregar el operador ternario, es una obviedad para mí
e inventé algunos ejemplos que serían útiles para mí personalmente y creo que puedes relacionarte un poco con algunos de ellos.

const PORT = production ? 80 : 8080
en lugar de

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

fmt.Printf("Running %s build!", production ? "Production" : "Debug") .. etc

Pero, por supuesto, el operador ternario es muy difícil de aprender.

Sí, ese es un buen ejemplo, especialmente si está en el nivel superior:

const production = true

//...

const PORT = production ? 80 : 8080

ya que no necesita una función init para inicializar PORT.

Una función incorporada de cond _podría_ ser capaz de usarse como un inicializador const aunque una versión genérica definitivamente no podría.

@Terottaja @alanfo

Creo que la solución idiomática a ese problema específico es usar Build Constraints .

@Terottaja

Vea mi otro comentario para la solución para variables/constantes globales.

Para variables/constantes locales, creo que la forma idiomática de escribir este código es:

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

es:

PORT := 8080
if production {
    PORT = 80
}

Podría argumentar que cambié la const a una var , pero me sorprendería si el compilador no fuera lo suficientemente inteligente ™ para darse cuenta de que PORT es constante, por lo que, en mi opinión, no hace ninguna diferencia en el código real.

Aunque probablemente no importaría en este ejemplo en particular si PORT fuera un const o un var , de manera más general podría importar. Por ejemplo, si estuviera declarando un número entero que se usaría para definir el tamaño de una matriz.

Quizá debería dejar clara mi propia posición sobre esta propuesta. Si bien personalmente no tengo ningún problema con el operador ternario 'estándar', no estoy seguro de que sea una buena idea introducirlo en Go de esa forma. Preferiría mucho más un cond incorporado en su lugar, que es mucho más legible, aunque, de manera realista, veo pocas posibilidades de que se adopte.

@Terottaja

Vea mi otro comentario para la solución para variables/constantes globales.

Para variables/constantes locales, creo que la forma idiomática de escribir este código es:

const PORT = -1
if production {
  PORT = 80
else {
  PORT = 8080
}

es:

PORT := 8080
if production {
  PORT = 80
}

Podría argumentar que cambié _const_ a _var_, pero me sorprendería si el compilador no fuera _lo suficientemente inteligente ™_ descubra que _PORT_ es constante, por lo que, en mi opinión, no hace ninguna diferencia en el código real.

solo estoy hablando de que si tiene muchas cosas de este tipo en su código, el operador ternario definitivamente sería más limpio en este caso, solo mi opinión

En mi opinión, escribir más código (solo unas pocas líneas) es mejor que averiguar la regla de x ? a : b . La declaración if puede parecer detallada (no estoy seguro) pero fácil de entender.

Además, se puede abusar fácilmente de un operador condicional ternario cuando las personas escriben varios x ? a : b anidados. El beneficio de presentarlo no es lo suficientemente grande.

siempre habrá personas que abusen de él, se puede abusar del código, es inevitable, pero ¿eso te afectará? en la mayoría de los casos, no

Apoyo esta función, aunque puede conducir a abusos en el código, es realmente beneficiosa para las optimizaciones del compilador, cuando evitas el if /else genérico y lo reemplazas con un operador ternario.
La gente ha estado haciendo cambios bit a bit desde el principio (quién puede culparlos, y todavía nadie sugiere que eliminemos los cambios bit a bit debido a la legibilidad), y el operador ternario es crucial en la actualidad.

@Lexkane : el compilador ya tiene optimizaciones que usan movimientos condicionales. No necesitamos una construcción de lenguaje para forzar tales optimizaciones. Por ejemplo, el siguiente código usa uno:

func f(x, y int) int {
    r := 3
    if x < y {
        r = 7
    }
    return r
}

Si tiene instancias particulares en las que no se genera un movimiento condicional, y cree que debería, abra un problema con el código.

¡Desde que uso Go y Javascript en mi trabajo al mismo tiempo, ha habido innumerables veces que he querido escribir x ? a : b en los programas de Go! ¡Debería haberlo escrito para mostrarle todos estos casos a @ianlancetaylor ! Todo eran programas reales.
El operador ternario es el que todos aprendemos en la escuela (ni siquiera en la universidad), por lo que en su forma clásica es la forma natural de escribir y leer código.
Todos aprendimos que hay tres tipos de operadores: unarios, binarios y ternarios. Go carece de un tipo sin motivo real, en mi opinión.
Ambas manos para x ? a : b .

Todos aprendimos que hay tres tipos de operadores: unarios, binarios y ternarios. Go carece de un tipo sin motivo real, en mi opinión.

Sin embargo, no hay razón para que tenga que haberla. 'ternario' es solo una palabra en inglés que significa 'compuesto de cuatro partes'. También podría tener fácilmente operadores cuaternarios o quinarios.

Personalmente, siento que los operadores ternarios por defecto son molestos de leer. Con los operadores binarios y unarios, puede ver fácilmente dónde está todo, pero con el ternario no siempre está claro qué va con qué, especialmente una vez que comienza a anidarlos. Puedo ver el argumento para que sean más limpios en situaciones específicas, pero fuera de esas situaciones, casi siempre son peores. gofmt podría ayudar, potencialmente, pero solo si fuera mucho más agresivo sobre cómo reformatear el código de lo que es. Tal vez se podría introducir algún tipo de limitación, como prohibir el anidamiento o encadenarlo, pero en ese momento no estoy seguro de si realmente vale la pena.

Ya se ha dicho que uno puede hacer el lío con el conjunto más simple de operadores. Es cierto que el código Go es más sencillo de leer y escribir que el código Java o Javascript. Pero no es imposible hacerlo ilegible.
Por lo tanto, el operador ternario es principalmente para frases de una sola línea y debe usarse para esos casos.
De lo contrario, puede tomar "if - then - else", anidarlo varias veces y hacer que su código sea un desastre total. Siempre depende de ti.
Creo que la gente subestima la frecuencia con la que surgen expresiones de una sola línea en el código. A veces son raros, pero a veces llenan la mitad del código escrito y, en un caso posterior, quiero tener un operador ternario preferiblemente en la forma en que está en Javascript.

Me gusta que en Go, el flujo de control generalmente se realiza con declaraciones, no con expresiones (la invocación de funciones es la excepción obvia). Muchos lenguajes populares se esfuerzan por "todo es una expresión", lo que a menudo es divertido, pero imo fomenta la escritura de código "inteligente". Ir, para mí, se trata de minimizar la inteligencia.

Por cierto, otra forma (asquerosa) de implementar una expresión ternaria es:

map[bool]string{true: "", false: "s"}[nbFriends == 1]
map[bool]string{true: "", false: "s"}[nbFriends == 1]

buen truco pero mucho más "inteligente" que el simple y conocido anuncio obvio ? : .

Algunas características clásicas pueden ser dañinas, pero ya nos hemos acostumbrado a ellas. Ni siquiera nos damos cuenta.

La condición debe ser un bool, por lo que a veces tenemos que escribir

x := 0
y := x != 0 ? 1 : 2

No es intuitivo. Porque en la instrucción if ves if a primera vista y sabes que habrá una condición.

En expresión ternaria, solo si ve ? sabrá que es una condición. Te sorprenderás y volverás a leer la condición nuevamente cuando sea complicada.

Rompe el flujo de lectura de izquierda a derecha, de arriba a abajo.

Podría argumentar que cambié _const_ a _var_, pero me sorprendería si el compilador no fuera _lo suficientemente inteligente ™_ descubra que _PORT_ es constante, por lo que, en mi opinión, no hace ninguna diferencia en el código real.

Bueno, hace la diferencia al perder la consistencia. Const no se trata solo de optimización. El código más abajo ahora puede alterar el valor

Qué tal: permitir ternario pero rechazar anidarlos.
Me gustaría eso.

La única razón por la que something ? foo : bar _parece_ legible es porque ya estamos acostumbrados a usarlo en otros idiomas. Pero de ninguna manera eso es más legible o más claro que

if something {
  foo
} else {
  bar
}

especialmente para los recién llegados para quienes Go es su primer idioma.

Si la legibilidad no es mejor, la única ganancia posible de esto es escribir menos líneas de código. Para mí, eso no parece una buena razón para introducir una construcción alternativa no muy intuitiva para algo que ya tenemos.
Ya tenemos if . ¿Por qué agregar otra forma menos intuitiva de hacer lo mismo?

if something {
  foo
} else {
  bar
}

'else' también rompe la línea de visión y la legibilidad (tiene que volver y leer si la condición nuevamente). Será mejor que me deshaga de 'else' en favor de '?'.

La única razón por la que something ? foo : bar _parece_ legible es porque ya estamos acostumbrados a usarlo en otros idiomas. Pero de ninguna manera eso es más legible o más claro que

if something {
  foo
} else {
  bar
}

Bueno, es más claro de varias maneras:

  • reduce la ceremonia a lo que es esencialmente una operación trivial,
  • expresa una asignación en una línea,
  • nos permite mantener la variable como "const" (y así reducir la carga mental y la posibilidad de jugar con ella más tarde),
  • son 5 líneas menos y, por lo tanto, facilita la incorporación de más código.

especialmente para los recién llegados para quienes Go es su primer idioma.

¿Por qué un idioma centraría su expresibilidad en los recién llegados? Es como hacer una interfaz de usuario simplista solo para los analfabetos informáticos y, por lo tanto, frustrar a cualquiera que supere un cierto punto y pierda la potencia adicional.

@bugpowder La expresión ternaria es totalmente reemplazable, y la instrucción if es más intuitiva, más expresiva y más común.

@bugpowder La expresión ternaria es totalmente reemplazable, y la instrucción if es más intuitiva, más expresiva y más común.

¿Qué pasa con los casos como;

println("Example bool is: ", bool ? "true" : "false")
esto es realmente donde brilla para mí donde realmente no puedes usar la instrucción if a menos que quieras que tu código se vea así:

bool := "false" if bool { bool = "true" } println("Example bool is: ", bool ? "true" : "false")

La única razón por la que something ? foo : bar _parece_ legible es porque ya estamos acostumbrados a usarlo en otros idiomas. Pero de ninguna manera eso es más legible o más claro que

if something {
  foo
} else {
  bar
}

especialmente para los recién llegados para quienes Go es su primer idioma.

Si la legibilidad no es mejor, la única ganancia posible de esto es escribir menos líneas de código. Para mí, eso no parece una buena razón para introducir una construcción alternativa no muy intuitiva para algo que ya tenemos.
Ya tenemos if . ¿Por qué agregar otra forma menos intuitiva de hacer lo mismo?

Cuando aprendí a codificar, el operador ternario parecía un poco extraño y complejo al principio, pero después de investigarlo más y ver estos ejemplos como:

true ? "it's true!" : "It's false!"
parecía realmente lógico y simple, pero así soy yo. quizás para otros sea suuuper complejo y difícil de entender.

No estoy de acuerdo con la convención de Go y los argumentos de los diseñadores del lenguaje aquí https://golang.org/doc/faq#Does_Go_have_a_ternary_form y creo que es una característica real que falta en el lenguaje.

Pero la última oración de una pregunta frecuente explica claramente '¿por qué no hay?:':

Un lenguaje necesita solo una construcción de flujo de control condicional.

La capacidad de exprimir más lógica en una línea no aporta nada nuevo. Ya existe una forma limpia de cambiar el flujo de control, otra es simplemente excesiva.

Veamos lo que está mal con la expresión ternaria. En un caso simple, ambos están bien.

var a, b int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? "true" : "false")
var a, b int
var c string
if a != 0 && b != 0 {
        c = "true"
} else {
        c = "false"
}
fmt.Println("Example bool is: ", c)

Cuando las cosas se vuelven complejas (agregue una condición), la expresión ternaria parece ambigua.

Sin embargo, el formulario if-else hace su trabajo. Creo que una forma más corta no siempre es la forma más clara.

var a, b, c int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? c != 0 ? "true" : "false" : "false")
var a, b, c int
var d string
if a != 0 && b != 0 {
        if c != 0 {
                d = "true"
        } else {
                d = "false"
        }
} else {
        d = "false"
}
fmt.Println("Example bool is: ", d)

La expresión ternaria es solo un caso especial de la declaración if. No vale la pena agregar una nueva sintaxis para un caso especial simple.

¡Sí, por favor, sí! Me encanta Go y se centra en la legibilidad, pero tiene un precio como código muy detallado.
Creo firmemente que el operador ternario mejorará la "capacidad de escritura" sin dañar la legibilidad. Simplemente hará que el código de Go sea un poco más elegante y conveniente.

Hablando de ambigüedad y otros problemas de codificación, diría que los desarrolladores con malas prácticas de codificación escribirán código incorrecto sin operador ternario de todos modos.

@ziflex En mi humilde opinión, la mejor práctica de codificación para evitar la ambigüedad es NO usar una expresión ternaria incluso en un idioma con expresión ternaria.

Hay trampas que debe evitar, por lo que debe aprender prácticas de codificación como

  1. no lo anide.
  2. solo utilízalo en un caso realmente simple.
  3. cuando la lógica se vuelva compleja, reescríbala en forma if-else.
    ...

Estas trampas te llevarán por encima de la cabeza.

@DongchengWang Algunas de estas prácticas se pueden aplicar fácilmente a simples declaraciones "si".

Muchos respondedores dicen que el ternario es reemplazable y no trae nada nuevo.

Pero al mismo tiempo veo cuántas personas usan “if err := mayError(); err != nil {}” construcción que también se puede reescribir fácilmente con simples declaraciones “if”. Pero por alguna razón, nadie realmente se queja de eso. ¿Por qué? Porque es conveniente. Y probablemente porque estaba en el idioma desde el principio.

La única razón por la que estamos discutiendo es porque el operador no estuvo allí desde el principio.

Personalmente, ha habido algunas ocasiones en las que tener una expresión ternaria habría dado como resultado un código menos anidado y de aspecto más limpio. Sin embargo, solo unos pocos, así que no estoy casado con esto ...

Para evitar la complejidad adicional de la sintaxis mediante el uso de los caracteres ?: más o menos estándar, ¿qué hay de reutilizar palabras clave de idioma existentes como en el caso del bucle for de la siguiente manera?

port := 80 if production else 8080

Todavía te permite encadenarlo y abusar de él, pero solo tanto como puedas con un bloque tradicional if/else ...

Creo que aún deberías meditar sobre los principios de por qué nació este lenguaje: simple, una forma obvia de hacer las cosas, características ortogonales y bibliotecas.

Creo que estamos en algo así como el 70% allí. No todo es perfecto.

Lo que me gusta de Go hasta ahora es el hecho de que disminuye la discusión entre los miembros del equipo sobre la forma de codificar, sobre el estilo de código que cada uno de ellos puede usar (¿recuerdas varias versiones de PSR en PHP?). Nosotros, en nuestro equipo de 7 miembros, nunca discutimos sobre los códigos de los demás. Solo nos enfocamos en nuestro objetivo.

No es que no me guste el operador condicional ternario, pero necesito oponerme a agregarlo y algo así a Go porque no me gusta que la forma de codificar se convierta en uno de nuestros argumentos.

Azúcar innecesaria. Creo que es uno de esos momentos en los que estamos tratando de traer algo que "fue usado en otro idioma". No se siente Ir.

Como se señaló anteriormente, existe una entrada de preguntas frecuentes que explica por qué el idioma no tiene el operador condicional. El tema no tiene un fuerte apoyo. Agregar una nueva característica al idioma nunca puede hacerlo más simple. Una vez que hay dos formas de hacer algo ( if o ?: ), cada programador a menudo tendrá que decidir qué forma usar, lo cual no es bueno. En general, no vemos ningún argumento nuevo aquí.

Por lo tanto, esta es una probable disminución . Dejando abierto durante un mes para comentarios finales.

Para el punto de @ziflex , no estoy seguro de cómo una línea detallada si:

if bool := operation(); bool {}

es muy diferente de un operador ternario. Es una construcción a la que no renunciaría felizmente, pero tiene la posibilidad de sufrir la misma complejidad de un eval ? a : b como:

func main() {
    if a := A(); !B(a) && !C(a) && D(C(a)) {
        fmt.Println("confused")
    }
}

func A() bool {
    return true
}

func B(in bool) bool {
    return !in
}

func C(in bool) bool {
    return in
}

func D(in bool) bool {
    return in
}

Esto solo sirve para ilustrar que un código mal escrito en una sola línea puede volverse tan ilegible como algunos de los ejemplos anteriores. La capacidad de abusar de una sintaxis, en mi humilde opinión, no debería ser motivo suficiente para evitar que se agregue al idioma un operador significativamente utilizable.

Sin embargo, el formulario if-else hace su trabajo. Creo que una forma más corta no siempre es la forma más clara.

var a, b, c int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? c != 0 ? "true" : "false" : "false")

Ese es un mal ejemplo, porque omitió el paréntesis, lo que hace que su ejemplo suene extremadamente ofuscado mientras que podría ser comprensible.

Realmente pienso
go var a, b, c int fmt.Println("Example bool is: ", (a != 0 && b != 0 ? (c != 0 ? "true" : "false") : "false"))

es más legible que

var a, b, c int
var d string
if a != 0 && b != 0 {
        if c != 0 {
                d = "true"
        } else {
                d = "false"
        }
} else {
        d = "false"
}
fmt.Println("Example bool is: ", d)

(Incluso si su ejemplo en realidad podría ser aún más simple, pero supongo que ese no es el punto:)

go fmt.Println("Example bool is: ", (a != 0 && b != 0 && c != 0 ? "true" : "false"))

Aunque ocasionalmente me encuentro queriendo un condicional dentro de otras expresiones o declaraciones, casi siempre siento que sería una pérdida neta de legibilidad.

Lo único que lamento es la falta de una forma de tener una inicialización condicional donde ambas ramas no serían un valor cero. Sé que el compilador probablemente sea lo suficientemente inteligente como para que esto no sea un desperdicio, pero mi sentido de lo que hace la máquina abstracta me dice que estoy poniendo a cero un valor y luego sobrescribiéndolo inmediatamente. Este es... un argumento bastante débil, de verdad.

FWIW, no encuentro la forma ternaria de los ejemplos "Example bool" más legibles, sobre todo porque, en mi pantalla en este momento, terminan requiriendo desplazamiento horizontal para incluso ver la expresión completa. Incluso haciendo eso, tengo que mirar hacia adelante y hacia atrás con mucha más frecuencia para descubrir qué está haciendo.

La respuesta obvia de algunos de los lenguajes de secuencias de comandos es que las declaraciones if sean expresiones que se puedan asignar y, de hecho, me gusta mucho ese diseño para esos lenguajes, pero no creo que me guste tanto para ir.

Supongo que siempre hay:

x := func() int {
    if a {
        return 1
    }
    return 2
}()

... pero ahora que lo pienso, si pudiéramos simplificar eso, en realidad sería bastante útil para circunstancias como esta. Algo que nos permita expresar "esta función es en realidad meramente notacional, no hay necesidad de generar código de función, solo queremos una forma de usar expresiones de retorno en un bloque interno"...

El lenguaje Go nace para la sencillez. ¿Por qué quieres hacer estas cosas elegantes? Siente que ha escrito algunas líneas de código, pero agrega más complejidad y confusión al código.
Entonces no apoyo

No es "elegante", es "simple", por lo que encaja perfectamente. Es familiar porque lo usamos en muchos otros idiomas. Es conveniente porque es una expresión de una sola línea con poco tipeo. Es importante tenerlo porque es una construcción común que usaríamos mucho.

Entonces preferiría usar una lista de comprensión como python:
a if a>1 else b
En lugar de todo tipo de símbolos extraños, como Rust.
Preferiría escribir más código para expresarlo que usar estos extraños símbolos para omitir el código.
El código es para que la gente lo lea.

No agregue nuevas funciones gramaticales para una situación que no se usa comúnmente, porque está destruyendo la intención original de Go, es decir, simple y fácil de leer.
Creo que, a veces, mucha gente es egoísta, por su conveniencia en ciertas situaciones, o porque se echan a perder por las funciones de conveniencia proporcionadas por otros lenguajes de programación, tiene que agregar sus funciones favoritas en Go.
Lo que quiero decir es que el Go no es tu idioma, el Go tiene su propio camino por recorrer.
Aunque puedes inventar tu propio idioma, puedes.

@Yanwenjiepy Parece que también te has convertido en un "purista" de Go que, en mi opinión, está obstaculizando el progreso.

I would rather write more code to express it than to use these strange symbols
Puede ser extraño para usted, pero no extraños símbolos para la mayoría de nosotros que estamos familiarizados con cualquier lenguaje común basado en C; C, C++, C#, Java, JavaScript, etc. Todos tienen la expresión ternaria.

Please don't add new grammar functions for a situation that is not commonly used
¡Las declaraciones condicionales ternarias son realmente muy útiles y se usan comúnmente!

Solo para ser exigente, eso no es una "lista de comprensión". Una lista de comprensión es específicamente algo así como [x for y in z] (posiblemente más condiciones). El uso de if/else en expresiones es una característica diferente.

Estoy muy familiarizado con los operadores ternarios y he usado lenguajes que los tenían durante la mayor parte de mi vida, pero creo que aproximadamente el 95 % de los usos que he visto en otros idiomas no encajarían bien con lo que hace que Go sea agradable para trabajar. in. Go ha tendido a evitar la densidad de información de algunos tipos, como los operadores de preincremento/postincremento que se pueden usar en expresiones, y creo que el operador ternario tiene el mismo problema subyacente; se necesita demasiado espacio para pensar en lo que hace.

El compilador es razonablemente inteligente. Puede declarar un valor, asignarlo condicionalmente, usarlo una vez y esperar que el compilador funcione tan bien como lo haría con una expresión ternaria.

@Yanwenjiepy Parece que también te has convertido en un "purista" de Go, lo que en mi opinión dificulta el progreso.

I would rather write more code to express it than to use these strange symbols
Para la mayoría de nosotros que estamos familiarizados con cualquier lenguaje C común, esto puede ser una notación extraña, pero no extraña; C, C++, C#, Java, JavaScript, etc. Ambos tienen expresiones ternarias.

Please don't add new grammar functions for a situation that is not commonly used
¡Las declaraciones condicionales ternarias son realmente muy convenientes y de uso común!
Tal vez, yo mismo tengo un sentimiento similar. Tal vez en otros temas, no soy un 'purista'. Esto es muy confuso.

@seebs Ciertamente puedo respetar esa opinión, pero para muchos de nosotros que venimos de otros lenguajes basados ​​​​en C, "agradable para trabajar" significa productividad a través de la familiaridad y la conveniencia. Nunca pude comprender por qué i++ en Go es menos agradable/conveniente que i = i + 1 , especialmente cuando un incremento posterior está bien en los bucles, por ejemplo, for i := 0; i < 5; i++ {...} pero no está bien como afirmación. ¡Demasiado purismo digo! :)

¿De qué estás hablando? i++ está perfectamente permitido como declaración en Go. lo que no está permitido es usarlo en una _expresión_, donde se evalúa tanto por un valor como por un efecto secundario.

https://play.golang.org/p/m_LbSbmT1Ar

Como alguien razonablemente familiarizado con C, no obstante encuentro que estoy bien sin el operador ternario, y me gusta más el código resultante. También dejé de usarlo tanto en C, al igual que me volví más consistente en el uso de llaves en todos los bloques, no solo bloques con más de una declaración en ellos.

No quise decir i++ por sí solo, quise decir como una expresión como fmt.Printf("%d", i++) que sería conveniente para algunos de nosotros.

Sí, definitivamente es conveniente, pero es, claramente, menos mantenible. La gente comete errores con él, la gente malinterpreta el código que lo usa. Es una fuente de errores, y simplemente no agrega mucho valor.

Sí, si quiero hacer x++ , tengo que hacerlo como su propia declaración antes o después de Printf . Eso es de hecho un costo, en absoluto. Pero a cambio, obtengo:

  • De repente, x no obtiene los valores incorrectos si comento algunas de las llamadas de Printf.
  • Los cambios en los mensajes de formato no rompen la lógica de mi programa.
  • Alguien más hojeando el código (o yo sin suficiente café) simplemente no se perderá que el incremento está ahí.

Es una compensación, pero creo que es bastante buena. Paso parte de mi tiempo tratando de responder preguntas de programación de novatos, porque eso es parte de cómo desarrollamos comunidades lingüísticas más saludables. Dedico mucho menos tiempo tratando de descifrar las sutilezas de la puntuación en el código Go de las personas que en su código C, y tienen muchos menos problemas causados ​​completamente por errores tipográficos sutiles.

Les aseguro que esto no es purismo viniendo de personas que no entienden o aprecian C. Es una decisión considerada que este lenguaje parece tener mucho valor simplemente por no ser tan complicado, y eso nos deja más espacio para ser haciendo cosas complicadas con nuestra lógica ya que no gastamos mucho de nuestro esfuerzo analizando el código.

Te escucho, pero no estoy convencido de todo eso. Por ejemplo, lo siguiente funciona en Go y, según su argumento, debería ser la única sintaxis permitida:

    for i:=0; i<5; i=i+1 {
      fmt.Printf("%d\n", i)
    }

Pero también se permite la sintaxis más popular/familiar:

    for i:=0; i<5; i++ {
      fmt.Printf("%d\n", i)
    }

¿Por qué crees que es?

De hecho, mi argumento no establece que sólo deba permitirse el primero. Me gusta más el segundo, es más simple y más fácil de leer, porque el operador de incremento es algo independiente, no un efecto secundario .

Tenga en cuenta que no puede hacer:

i := 0
for i++ < 5 {
    ...
}

Porque Go no le permite poner asignaciones o incrementos en expresiones. Y eso puede ser un inconveniente a veces, pero la frecuencia con la que no da lugar a que las personas malinterpreten una expresión y no se den cuenta de que modifica los valores es básicamente del 100%, lo cual es bueno.

Mira, eso es demasiado purista para mí :) De todos modos, mi punto es que, dado que tanto i=i+1 como i++ están permitidos en los bucles, digo que también permita una variación ternaria para aquellos que prefieren la conveniencia de una sola línea , p.ej

```Ve
Puerto := producción ? 80: 8080

as well as the usual:

```Go
Port := 8080
if production {
    Port = 80
}

Si se trata de simplicidad, creo que lo primero es más simple.

Qué pasa:

Port := production++

o

fmt.Printf("port: %d\n", production++)

Ambos también son más cortos usando el idioma C de lo que serían en Go. Pero diría que en ambos casos, la posibilidad de que exista esa complejidad hace que todo el programa sea un poco más difícil de entender; ahora debe estar atento a esos efectos todo el tiempo.

A un nivel filosófico más fundamental: el problema es que su solución no es solo para aquellos que prefieren esa "conveniencia". También se impone por la fuerza a todos los demás, para siempre. Porque todos tienen que leer el código de otras personas . Por lo tanto, las personas no pueden optar por no recibir una función como esta. No pueden decir "bueno, eso es más difícil de mantener para mí, así que no lo usaré" y no tener que lidiar con eso. Están atrapados en que sea parte de su mundo. Cada vez que leen código en el que cualquier otra persona podría haber trabajado, tienen que estar atentos a un nuevo conjunto de complejidades.

En la práctica, esta "simplicidad" tiene un costo significativo, porque en realidad no es simplicidad , es simplemente acortar la expresión. Es como el if sin tirantes de una sola línea; parece que es más simple, pero luego necesita una segunda línea y hay una probabilidad distinta de cero de que se olvide de agregar las llaves en ese momento, y muy pronto habrá perdido más tiempo del que hubiera tenido simplemente colocando las llaves allí.

Sé lo que pasa si escribes:

Port := production ? 80 : 8080

Unos días más tarde:

Port := production ? 80 : test ? 4080 : 8080

Pero luego alguien se da cuenta de que los dos bools son una mala elección y lo arregla:

Port := mode == "production" ? 80 : mode == "test" ? 4080 : 8080

y debido a que era solo una línea, y al usar ?: , las personas sienten que alargarlo es un esfuerzo adicional, y no lo arreglan ni lo limpian. Y ahora tienen una inversión en que sea de esa manera.

Y así es como terminas con ?: operaciones anidadas 15 de profundidad, que he visto en el código real, que absolutamente deberían haber sido tablas de búsqueda.

¿Y así es como terminas con ?: operaciones anidadas 15 de profundidad, que he visto en el código real, que absolutamente deberían haber sido tablas de búsqueda.

Sin embargo, las operaciones "if-else" anidadas en 15 de profundidad también deberían haber sido tablas de búsqueda.

Oh, ciertamente.

Pero si tiene operaciones if/else anidadas de 15 profundidades y convierte a una tabla de búsqueda, no siente que ha perdido la "simplicidad" de la solución de una sola línea.

El problema es que su solución no es solo para aquellos que prefieren esa “comodidad”. También se impone por la fuerza a todos los demás, para siempre.

No podría estar más en desacuerdo porque la verdad es todo lo contrario! En realidad, es su visión purista la que impone su forma de hacerlo y limita mi libertad para elegir una versión abreviada . Si no quieres usarlo, es tu elección, ¡pero no limites mi elección!

Si puedo optar por escribir un a := "freedom" abreviado en lugar de var a string = "freedom" entonces debería tener la libertad y la comodidad de una asignación ternaria.

Las herramientas de Go hacen un gran trabajo al estandarizar el formato del código y creo que solo eso hace que sea bastante fácil leer el código de otras personas.

La conclusión para mí es que encuentro que las asignaciones ternarias son más fáciles de leer y comprender porque se traducen de forma más natural al inglés. Creo que es por eso que es tan popular en muchos otros idiomas. A mi esto:

port := production ? 80 : 8080

... se traduce como: "¿Esto es producción? Si es así, el puerto es 80 y si no, el puerto es 8080"
(una sola asignación simple y directa, incluso cuando está anidada)

port := 8080
if production {
    port = 80
}

Esto se traduce como: "el puerto es 8080 (punto) Oh, pero si esto es producción, cambie el puerto a 80" (segunda asignación).

El segundo definitivamente NO es más fácil de leer para mí. Nunca lo ha sido.

Si es el ? y: eso molesta a la gente, también estaría feliz con cualquier sintaxis alternativa de una sola línea.

Esta construcción de una sola línea funciona para valores iniciales no calculados. Una forma de extender esto a los valores iniciales calculados sería genial.

v := a; if t { v = b }        // non-computed initial value

v := f(); else if t { v = b } // f() not evaluated where t==true

Lamentablemente, _go fmt_ destruye muchas construcciones útiles de una sola línea. No uso _go fmt_ por ese motivo; El código compacto legible telescópico lo hace menos legible. Pero eso es tangencial.

Esta funcionalidad se puede lograr sin un operador ternario si Go 2 agrega Genéricos

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

Por supuesto, usar esto no sería agradable, especialmente si hubiera más de una llamada ternary .

En cuanto a la evaluación, esto puede ser una optimización a nivel del compilador.

Esta funcionalidad se puede lograr sin un operador ternario si Go 2 agrega Genéricos

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

Por supuesto, usar esto no sería agradable, especialmente si hubiera más de una llamada ternary .

En cuanto a la evaluación, esto puede ser una optimización a nivel del compilador.

Hum no, vTrue y vFalse siempre serán evaluados, quiero decir

ternary(3>2, func1(), func2())

provocará la llamada tanto de func1() como de func2(). Ningún compilador podría saber que func2() no necesita ser evaluado... y nunca debería asumir eso de todos modos, porque es un principio fundamental que en una llamada de función siempre se espera que los argumentos sean evaluados, antes de la llamada de la función. sí mismo. Si func2() hace cosas además de devolver un valor, queremos que esto se haga, de lo contrario sería muy impredecible y difícil de comprender.

(Al contrario de un operador ternario real donde se supone que el valor de falso no debe evaluarse por principio).
```

Esta funcionalidad se puede lograr sin un operador ternario si Go 2 agrega Genéricos

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

Por supuesto, usar esto no sería agradable, especialmente si hubiera más de una llamada ternary .
En cuanto a la evaluación, esto puede ser una optimización a nivel del compilador.

Hum no, vTrue y vFalse siempre serán evaluados, quiero decir

ternary(3>2, func1(), func2())

provocará la llamada tanto de func1() como de func2(). Ningún compilador podría saber que func2() no necesita ser evaluado... y nunca debería asumir eso de todos modos, porque es un principio fundamental que en una llamada de función siempre se espera que los argumentos sean evaluados, antes de la llamada de la función. sí mismo. Si func2() hace cosas además de devolver un valor, queremos que esto se haga, de lo contrario sería muy impredecible y difícil de comprender.

(Al contrario de un operador ternario real donde se supone que el valor de falso no debe evaluarse por principio).

Entonces la firma quedaría así:

func ternary(type T)(cond bool, vTrueFunc, vFalseFunc func() T) T { 
    if cond { return vTrueFunc() } else { return vFalseFunc() }
}

Sin embargo, debo admitir que esta implementación es bastante fea :(

Hubo comentarios adicionales desde que declaramos que esto era un probable declive (https://github.com/golang/go/issues/33171#issuecomment-525486967), pero hasta donde sabemos, ninguno de ellos dijo nada sustancialmente nuevo. Estamos de acuerdo en que hay casos en los que la sintaxis ?: sería conveniente, pero en general no parece que valga la pena agregarla al lenguaje.

-- para @golang/propuesta-revisión

Entonces, ¿la decisión se basa en "no parece que valga la pena agregar"?
No me sorprende en absoluto... Las expresiones ternarias destruirían la "pureza" del lenguaje, ¿no?

Los cambios de idioma son siempre una compensación de costo-beneficio. Rara vez hay una respuesta inequívoca.

No hay una explicación fácil con respecto a la ausencia del período del operador ternario.

A primera vista, la negativa a implementar esta característica básica es análoga a argumentar que las bicicletas pueden ser peligrosas si son rápidas y, como resultado, negarse a fabricar una bicicleta con marchas altas. Algunos pueden mezclarse en la conversación acerca de si los arquitectos de lenguajes de Go tienen razón o no, pero prefiero subir un nivel de abstracción y considerar si los lenguajes deberían combinar las preocupaciones de características con las preocupaciones de mantenimiento:

Si se puede abusar de la característica X y dar como resultado un nido de ratas de código, ¿es esa razón suficiente para que la característica X se excluya del lenguaje?

Yo diría que puede serlo, pero no por sí mismo: debe sopesarse frente a la demanda y la dificultad. Si una función tiene una gran demanda y es fácil de implementar, sería mejor desvincular las preocupaciones implementando la función y presentando una forma de rechazarla, e incluso rechazarla de forma predeterminada.

De hecho, si la demanda es lo suficientemente alta, incluso la dificultad es una mala razón para rechazarla. Considere la sintaxis de class en Javascript (ES2015): los arquitectos del lenguaje realmente no querían agregar la función, y en realidad fue bastante trabajo agregar la sintaxis, pero la demanda era increíblemente alta. ¿Que hicieron? Agregaron la sintaxis, sabiendo muy bien que cualquier organización que no quisiera la característica podría rechazar fácilmente la sintaxis en el nivel de pelusa.

Esta es la resolución apropiada para el operador ternario, dada la demanda. Es apropiado desacoplar estas preocupaciones, y resolverlas de una manera más configurable tendría más sentido. Lo mismo debería suceder con cosas como errores de "variable no utilizada" que hacen que las preocupaciones de pelusa se conviertan en crisis de "detener las prensas que necesita para arreglar esto antes de que el programa se ejecute". (Sí, sé que hay una solución de _ , pero aún así debería ser configurable)

Es un error pensar que un lenguaje debe ser el producto de un pequeño número de arquitectos que saben más sin una conexión profunda con aquellos que realmente están usando el lenguaje. El pedido de datos para probar que los arquitectos del lenguaje estaban equivocados es admirable, pero tal análisis es innecesario. Basta con mirar el tamaño de este hilo: hay demanda.

Desafortunadamente, ignorar la demanda es lo que conduce a productos competitivos: en este caso, así es como se bifurcan los idiomas. ¿Quieres que te bifurquen? (Para ser claros: esta es una predicción, no una amenaza.
Definitivamente no estoy bifurcando un idioma).

@dash Este problema está cerrado y no voy a discutirlo, pero me gustaría corregir lo que creo que es una tergiversación. Estás insinuando que Go es un lenguaje que es "... el producto de un pequeño número de arquitectos que saben mejor _sin una conexión profunda con aquellos que realmente usan el lenguaje_". Eso definitivamente no es cierto. Todos en el equipo de Go, y ciertamente los "arquitectos", escriben código de Go todos los días, y han estado escribiendo una cantidad sustancial desde 2007. También interactuamos con otros usuarios de Go casi a diario. Absolutamente tenemos una conexión profunda con aquellos que realmente usan el idioma, que somos nosotros, entre muchos otros.

No soy uno de los arquitectos y uso mucho el lenguaje, y regularmente encuentro situaciones en las que casi seguramente usaría un operador ternario si estuviera disponible. Y luego leo mi código más tarde, lo pienso y me alegro de que no esté allí. YMMV.

No creo que hacer que cosas como esta, o las advertencias de variables no utilizadas, sean "configurables" harían mi vida más fácil como desarrollador; Creo que me haría la vida más difícil como desarrollador.

Tampoco soy uno de los arquitectos, y también uso mucho el lenguaje, y regularmente me encuentro con situaciones en las que casi seguramente usaría un operador ternario si estuviera disponible. Y luego leo mi código más tarde, ¡y maldigo a las pocas personas que nos niegan esta característica útil!

Lo mismo aquí, uso Go todos los días y lo necesitaría todos los días y estoy convencido de que haría que mi código fuera más claro e incluso más sólido.

Por cierto, en la propuesta de "Reformular respuesta de preguntas frecuentes"

usar un operador ternario en lugar de una declaración condicional (no una expresión) por brevedad;

La "brevedad" se menciona como si fuera algo malo. La brevedad ayuda a la legibilidad. La idea general de un código legible es que es "directo al grano" de lo que realmente hace. No es como afectar 8080 o -1 al puerto, y luego 80 más adelante en el código porque esto es producción.

Creo que ahora hay pocas posibilidades de que Go obtenga un operador ternario, no solo porque el equipo de Go siempre ha argumentado en contra, sino porque (a juzgar por la votación de emoji) alrededor del 60% de la comunidad también está en contra.

Sin embargo, si Go finalmente obtiene genéricos, creo que el equipo debería considerar seriamente agregar una función ternaria a la biblioteca estándar, a pesar de que no habrá cortocircuito, excepto posiblemente por medio de la optimización del compilador.

Si no hacen esto, entonces el 40% que está a favor de algún tipo de operador/función termaria (incluido yo mismo) escribirá el suyo de inmediato. Esto creará una pesadilla de legibilidad y mantenimiento porque se elegirán diferentes nombres (Cond, Iff, Iif, Pick, Choose, Tern, etc.) y estarán en paquetes con diferentes nombres.

Si se agrega a la biblioteca estándar, entonces esta fragmentación no ocurrirá ya que todos los que estén a favor usarán la versión estándar y aquellos a quienes no les guste al menos sabrán lo que hace.

func ifThen(condición bool, ifTrue,ifelse interfaz{}) interfaz{}{
si la condición {
devolver si es verdadero
} demás {
volver si no
}
}

Me parece que esta discusión sobre el operador ternario en algunos casos se reduce a una "solución para un problema diferente".

La falta de valores predeterminados en las funciones da como resultado que las personas escriban código como:

if elementType == "" {
    elementType = "Whatever"
}
//  times X ...

con personas que quieren simplemente esto como:

elementType = elementType == "" ? "Whatever" : elementType
// times X ...

O

func DoDesign( elementType string = "Whatever" )

Entonces, un operador ternario intenta resolver un problema, que está relacionado con otro problema. Si bien la versión más estándar de Go es más legible, se vuelve menos legible cuando se trata de 4 o 5 de ellos seguidos.

También es necesario preguntarse si la legibilidad sirve cuando las personas comienzan a construir cada vez más sus propias "soluciones", como lo muestra @ArnoldoR . Uno de los problemas que aquejaban a Javascript es el crecimiento de "soluciones" para la funcionalidad faltante, lo que resultó en que algunos proyectos importaran paquetes NPM a diestra y siniestra. Los paquetes populares como SqlX son más una señal de que faltan funciones en Go.

La legibilidad es una cosa. Pero tener que escribir a veces 20 líneas que son más o menos pueden estar contenidas en 5 líneas. Se acumula en cualquier proyecto.

Si el problema es que el ternario se puede usar incorrectamente para crear un código ilegible, especialmente los operadores ternarios anidados, entonces ponle un límite. Creo que la mayoría de las personas en este tema no tendrán problemas si un operador ternario está limitado a solo "un nivel" y el compilador lo detiene de los operadores de nivel profundo.

Sabemos que la gente va a abusar muchísimo de los genéricos para implementarlos de todos modos. Entonces, ¿por qué no proporcionar una versión oficial, entonces puede limitar el abuso de la misma? Pero esas funciones salvajes, como vemos arriba, solo se volverán más populares con el tiempo, como hemos visto en la comunidad de Javascript. Y cuando el genio sale de la botella, ya no hay forma de controlarlo.

¿Fue útil esta página
0 / 5 - 0 calificaciones