Go: proposta: Go 2: adicione um operador condicional ternário

Criado em 18 jul. 2019  ·  78Comentários  ·  Fonte: golang/go

Eu discordo da convenção Go e dos argumentos dos designers da linguagem aqui https://golang.org/doc/faq#Does_Go_have_a_ternary_form e acho que é um recurso realmente ausente na linguagem.

Considere em C o seguinte código:

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

ou em C++:

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

Em Go, isso causa grandes repetições que podem causar erros, ou código muito detalhado e ineficiente, ou ambos, para algo que deve ser direto:

Solução 1:

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

Solução 2:

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

Solução 3:

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

Solução 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", ""))

Solução 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

Comentários muito úteis

Eu acho que o exemplo é meio ruim, considerando que é apenas sobre um caractere e não é realmente legível na minha opinião (?:"s":"")
mas eu concordo que o operador ternário deve ser adicionado, é um acéfalo para mim
e eu inventei alguns exemplos que seriam úteis para mim pessoalmente e acho que você pode se identificar com alguns deles.

const PORT = production ? 80 : 8080
ao invés de

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

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

Mas é claro que o operador ternário é muito difícil de aprender.

Todos 78 comentários

@gopherbot , adicione rótulo Go2, LanguageChange

Veja também #31659 e #32860.

Esta decisão já foi consagrada em um FAQ, como você observa. Se você quiser argumentar que a resposta do FAQ deve ser alterada, você precisa de mais do que alguns exemplos. Eu prometo a você que já vimos e consideramos esses exemplos. O que você precisa é de dados: programas reais com código que se tornaria mais simples e fácil de ler adicionando um operador condicional. Você também precisa de argumentos contra as preocupações comuns sobre o operador condicional, como dificultar a leitura do código, especialmente quando aninhado.

Além disso, um ponto menor, mas seu exemplo não é ótimo, pois funciona apenas para inglês e não oferece suporte à localização de strings de mensagens.

@ianlancetaylor

Em #31659 você fez o que eu pensei ser uma contra-sugestão muito boa de ter uma função interna cond para fornecer funcionalidade ternária. Isso precisava ser um built-in (em oposição a uma função genérica) para permitir a avaliação de curto-circuito dos argumentos verdadeiro/falso. Ele ainda sofria com a possibilidade de que as pessoas pudessem aninhar funções cond embora pessoalmente eu não considerasse isso um problema fatal porque, mesmo que o fizessem, ainda deveria ser mais legível do que os hieróglifos do próprio operador ternário do C .

Como essa proposta já foi encerrada, você pretende levar adiante essa sugestão ou desistiu da ideia de ter uma forma ternária alternativa?

Pessoalmente, não pretendo levar essa ideia adiante. Isso foi mais um pensamento de discussão do que uma sugestão séria. Claro, não me importo se alguém quiser transformar isso em uma proposta real. Mas, para ser aceito, acho que ainda precisaríamos ver alguns dados sobre o quanto isso simplificaria os programas reais existentes.

Certo, obrigado por esclarecer.

Basta olhar para o código em outras linguagens da família C para ver o quão comum é o operador ternário, mas seria difícil analisar o próprio código Go, pois, como @Phrounz apontou em seu post de abertura, várias construções são usadas para funcionar em torno de sua ausência.

Usando a ideia cond , seu exemplo se tornaria:

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

Dito tudo isso, se conseguirmos genéricos, eu pessoalmente me contentaria em escrever minha própria função cond e, dada a falta de curto-circuito, só a usaria onde os argumentos fossem baratos para avaliar.

Na minha opinião, escrever mais código (apenas algumas linhas) é melhor do que descobrir a regra de x ? a : b . A instrução if pode parecer detalhada (não tenho certeza), mas fácil de entender.

Além disso, um operador condicional ternário pode ser facilmente abusado quando as pessoas escrevem vários x ? a : b aninhados. O benefício de introduzi-lo não é grande o suficiente.

Eu vejo operadores ternários apenas sendo fáceis de visualizar em funções de uma linha. Mesmo assim, na maioria das vezes eles lidam com tratamento de erros, caso em que é melhor aderir a uma abordagem "quanto menos recuo, melhor" fazendo com que o erro ou o caminho menos provável para uma função seja encapsulado em um if e tratado então, em vez de ter lógica de ramificação múltipla.

Eu acho que o exemplo é meio ruim, considerando que é apenas sobre um caractere e não é realmente legível na minha opinião (?:"s":"")
mas eu concordo que o operador ternário deve ser adicionado, é um acéfalo para mim
e eu inventei alguns exemplos que seriam úteis para mim pessoalmente e acho que você pode se identificar com alguns deles.

const PORT = production ? 80 : 8080
ao invés de

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

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

Mas é claro que o operador ternário é muito difícil de aprender.

Sim, esse é um bom exemplo, principalmente se estiver no nível superior:

const production = true

//...

const PORT = production ? 80 : 8080

pois você não precisa de uma função init para inicializar PORT.

Uma função interna cond _pode_ ser capaz de ser usada como um inicializador const embora uma versão genérica definitivamente não pudesse.

@Terottaja @alanfo

Acho que a solução idiomática para esse problema específico é usar Build Constraints .

@Terottaja

Veja meu outro comentário para a solução para variáveis/constantes globais.

Para variáveis/constantes locais, acho que a maneira idiomática de escrever essa paz de código:

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

é:

PORT := 8080
if production {
    PORT = 80
}

Você pode argumentar que eu mudei o const para um var , mas ficaria surpreso se o compilador não fosse inteligente o suficiente ™ descobrisse que PORT é constante, então IMO não faz diferença no código real.

Embora provavelmente não importe neste exemplo em particular se PORT era um const ou um var , de forma mais geral pode importar. Por exemplo, se você estivesse declarando algum inteiro que deveria ser usado para definir o tamanho de um array.

Talvez eu devesse deixar minha própria posição clara sobre esta proposta. Embora eu pessoalmente não tenha nenhum problema com o operador ternário 'padrão', não tenho certeza se seria uma boa ideia introduzi-lo em Go dessa forma. Eu preferiria muito mais um cond embutido, que é muito mais legível, embora, realisticamente, eu veja pouca chance de ser adotado.

@Terottaja

Veja meu outro comentário para a solução para variáveis/constantes globais.

Para variáveis/constantes locais, acho que a maneira idiomática de escrever essa paz de código:

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

é:

PORT := 8080
if production {
  PORT = 80
}

Você poderia argumentar que eu mudei o _const_ para um _var_, mas eu ficaria surpreso se o compilador não fosse _inteligente o suficiente ™_ descobrir que _PORT_ é constante, então IMO não faz diferença no código real.

estou apenas falando que se você tiver muito desse tipo de coisa em seu código o operador ternário definitivamente seria mais limpo neste caso, apenas minha opinião

Na minha opinião, escrever mais código (apenas algumas linhas) é melhor do que descobrir a regra de x ? a : b . A instrução if pode parecer detalhada (não tenho certeza), mas fácil de entender.

Além disso, um operador condicional ternário pode ser facilmente abusado quando as pessoas escrevem vários x ? a : b aninhados. O benefício de introduzi-lo não é grande o suficiente.

sempre haverá pessoas abusando dele, o código pode ser abusado é inevitável, mas isso afetará você? na maioria dos casos, não

Eu apoio esse recurso, embora possa levar a abusos no código, é realmente benéfico para otimizações do compilador, quando você evita o genérico if /else e o substitui pelo operador ternário.
As pessoas têm feito mudanças bit a bit desde o início (quem pode culpá-las, e ainda ninguém sugere que vamos eliminar as mudanças bit a bit por causa da legibilidade), e o operador ternário é crucial para ter hoje em dia.

@Lexkane : O compilador já possui otimizações que usam movimentos condicionais. Não precisamos de uma construção de linguagem para forçar tais otimizações. Por exemplo, o código a seguir usa um:

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

Se você tiver instâncias específicas em que um movimento condicional não está sendo gerado, e você acha que deveria, abra um problema com o código.

Desde que eu uso Go e Javascript no meu trabalho ao mesmo tempo, foram inúmeras vezes que eu quis escrever x ? a : b em programas Go! Eu deveria ter escrito para mostrar todos esses casos para @ianlancetaylor ! Tudo eram programas reais.
O operador ternário é aquele que todos aprendemos na escola (nem mesmo na universidade), então em sua forma clássica é a maneira natural de escrever e ler código.
Todos nós aprendemos que existem três tipos de operadores: unário, binário e ternário. Go carece de um tipo sem motivo real IMO.
Ambas as mãos para x ? a : b .

Todos nós aprendemos que existem três tipos de operadores: unário, binário e ternário. Go carece de um tipo sem motivo real IMO.

Não há nenhuma razão que tem que ser, no entanto. 'ternário' é apenas uma palavra em inglês que significa 'composto de quatro partes'. Você poderia facilmente ter operadores quaternários ou quinários também.

Pessoalmente, sinto que os operadores ternários por padrão são irritantes de ler. Com operadores unários e binários, você pode facilmente ver exatamente onde está tudo, mas com os ternários nem sempre fica claro o que se passa com o quê, especialmente quando você começa a aninha-los. Eu posso ver o argumento para eles serem mais limpos em situações específicas, mas fora dessas situações eles são quase sempre piores. gofmt poderia ajudar, potencialmente, mas apenas se fosse muito mais agressivo sobre como reformatar o código do que é. Talvez algum tipo de limitação possa ser introduzido, como proibir o aninhamento ou encadeá-lo, mas nesse ponto não tenho certeza se realmente vale a pena.

Já foi dito que se pode fazer a bagunça com o conjunto mais simples de operadores. É verdade que o código Go é mais simples de ler e escrever do que o código Java ou Javascript. Mas não é impossível torná-lo ilegível.
Portanto, o operador ternário é principalmente para one-liners e deve ser usado para esses casos.
Caso contrário, você pode pegar "if - then - else", aninhá-lo várias vezes e tornar seu código uma bagunça total. Sempre depende de você.
Acho que as pessoas subestimam a frequência com que as expressões de uma linha surgem no código. Às vezes são raros mas às vezes preenchem metade do código escrito e em caso posterior quero ter operador ternário de preferência na forma que está em Javascript.

Eu gosto que em Go, o fluxo de controle geralmente é feito com instruções, não com expressões (a invocação de função é a exceção óbvia). Muitas linguagens populares se esforçam para "tudo é uma expressão", o que geralmente é divertido, mas o imo incentiva a escrita de código "inteligente". Go, para mim, tem tudo a ver com minimizar a esperteza.

A propósito, outra maneira (grossa) de implementar uma expressão ternária é:

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

bom truque, mas muito mais "inteligente" do que um simples anúncio bem conhecido ? : .

Alguns recursos clássicos podem ser prejudiciais, mas já nos acostumamos com eles. Nós nem percebemos.

A condição deve ser um bool, então às vezes temos que escrever

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

Não é intuitivo. Porque na declaração if você vê if à primeira vista e sabe que haverá uma condição.

Na expressão ternária somente se você vir ? você saberá que é uma condição. Você ficará surpreso e voltará a ler a condição novamente quando estiver complicada.

Ele quebra o fluxo de leitura da esquerda para a direita, de cima para baixo.

Você poderia argumentar que eu mudei o _const_ para um _var_, mas eu ficaria surpreso se o compilador não fosse _inteligente o suficiente ™_ descobrir que _PORT_ é constante, então IMO não faz diferença no código real.

Bem, isso faz a diferença perdendo a const-ness. Const não é apenas sobre otimização. Código mais abaixo agora pode mexer com o valor

Que tal: permitir o ternário, mas proibir o aninhamento deles.
Gostaria disso.

A única razão pela qual something ? foo : bar _parece_ legível é porque já estamos acostumados a usá-lo em outros idiomas. Mas de modo algum isso é mais legível ou mais claro do que

if something {
  foo
} else {
  bar
}

especialmente para os recém-chegados para quem Go é sua primeira língua.

Se a legibilidade não for melhor, o único ganho possível com isso é escrever menos linhas de código. Para mim, isso não parece uma razão boa o suficiente para introduzir uma construção alternativa não muito intuitiva para algo que já temos.
Já temos if . Por que adicionar outra maneira menos intuitiva de fazer a mesma coisa?

if something {
  foo
} else {
  bar
}

'else' também quebra a linha de visão e legibilidade (você tem que voltar e ler se condição novamente). É melhor eu me livrar de 'else' em favor de '?'.

A única razão pela qual something ? foo : bar _parece_ legível é porque já estamos acostumados a usá-lo em outros idiomas. Mas de modo algum isso é mais legível ou mais claro do que

if something {
  foo
} else {
  bar
}

Bem, é mais claro de várias maneiras:

  • reduz a cerimônia para o que é essencialmente uma operação trivial,
  • expressa uma atribuição em uma linha,
  • nos permite manter a variável como "const" (e assim reduzindo a carga mental e a possibilidade de mexer com ela mais tarde),
  • são 5 linhas a menos e, portanto, facilita a entrada de mais código.

especialmente para os recém-chegados para quem Go é sua primeira língua.

Por que uma linguagem focaria sua expressibilidade em recém-chegados? É como fazer uma interface de usuário simplista apenas para o computador analfabeto e, assim, frustrar qualquer um que vá além de um certo ponto e perca o poder extra.

@bugpowder A expressão ternária é totalmente substituível, e a instrução if é mais intuitiva, mais expressiva e mais comum.

@bugpowder A expressão ternária é totalmente substituível, e a instrução if é mais intuitiva, mais expressiva e mais comum.

E os casos em como;

println("Example bool is: ", bool ? "true" : "false")
isso é realmente onde brilha para mim, onde você realmente não pode usar a instrução if , a menos que queira que seu código fique assim:

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

A única razão pela qual something ? foo : bar _parece_ legível é porque já estamos acostumados a usá-lo em outros idiomas. Mas de modo algum isso é mais legível ou mais claro do que

if something {
  foo
} else {
  bar
}

especialmente para os recém-chegados para quem Go é sua primeira língua.

Se a legibilidade não for melhor, o único ganho possível com isso é escrever menos linhas de código. Para mim, isso não parece uma razão boa o suficiente para introduzir uma construção alternativa não muito intuitiva para algo que já temos.
Já temos if . Por que adicionar outra maneira menos intuitiva de fazer a mesma coisa?

Quando aprendi a codificar, o operador ternário parecia meio estranho e complexo no começo, mas depois de pesquisar mais e ver esses exemplos como:

true ? "it's true!" : "It's false!"
parecia muito lógico e simples, mas isso é só comigo. talvez para outros seja suuuper complexo e difícil de entender.

Eu discordo da convenção Go e dos argumentos dos designers da linguagem aqui https://golang.org/doc/faq#Does_Go_have_a_ternary_form e acho que é um recurso realmente ausente na linguagem.

Mas a última frase de um FAQ explica claramente 'por que não existe ?:`:

Uma linguagem precisa apenas de uma construção de fluxo de controle condicional.`

A capacidade de espremer mais lógica em uma linha não oferece nada de novo. Já existe uma forma limpa de alterar o fluxo de controle, outra é simplesmente excessiva.

Vejamos o que há de errado com a expressão ternária. Em um caso simples, ambos estão ok.

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)

Quando as coisas ficam complexas (adicione uma condição), a expressão ternária parece ambígua.

No entanto, a forma if-else faz seu trabalho. Eu acho que uma forma mais curta nem sempre é a forma mais 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)

A expressão ternária é apenas um caso especial da instrução if. Não vale a pena adicionar uma nova sintaxe para um caso especial simples.

Sim, por favor, sim! Eu amo Go e é focado na legibilidade, mas tem um preço como código muito detalhado.
Acredito fortemente que o operador ternário melhorará a “escrita” sem prejudicar a legibilidade. Isso apenas tornará o código do Go um pouco mais elegante e conveniente.

Falando de ambiguidade e outros problemas de codificação, eu diria que desenvolvedores com más práticas de codificação escreverão código ruim sem operador ternário.

@ziflex IMHO, a melhor prática de codificação para evitar ambiguidade é NÃO usar expressão ternária, mesmo em um idioma com expressão ternária.

Existem armadilhas que você deve evitar, então você precisa aprender práticas de codificação como

  1. não o aninhe.
  2. use-o apenas em um caso muito simples.
  3. quando a lógica se tornar complexa, reescreva-a na forma if-else.
    ...

Essas armadilhas vão trazer você para cima.

@DongchengWang Algumas dessas práticas podem ser facilmente aplicáveis ​​a simples declarações “se”.

Muitos respondentes dizem que o ternário é substituível e não traz nada de novo.

Mas ao mesmo tempo vejo quantas pessoas usam “if err := MaybeError(); err != nil {}” que também pode ser facilmente reescrita com instruções “if” simples. Mas por alguma razão, ninguém realmente reclama disso. Por quê? Porque é conveniente. E provavelmente porque estava na língua desde o início.

A única razão pela qual estamos discutindo sobre isso é apenas porque o operador não estava lá desde o início.

Pessoalmente, houve algumas ocasiões em que ter uma expressão ternária resultaria em um código mais limpo e menos aninhado. Apenas alguns, então eu não estou casado com isso ...

Para evitar a complexidade adicional da sintaxe usando os caracteres ?: mais ou menos padrão, que tal reutilizar palavras-chave de idioma existentes, como no caso do loop for da seguinte maneira?

port := 80 if production else 8080

Ele ainda permite que você encadeie e abuse, mas apenas o máximo que puder com um bloco tradicional de if/else ...

Acho que você ainda deve meditar sobre os princípios de por que essa linguagem nasceu: simples, uma maneira óbvia de fazer as coisas, recursos ortogonais e bibliotecas.

Acho que estamos em algo como 70% lá. Nem tudo é perfeito.

O que eu gosto no Go até agora é o fato de que ele diminui a discussão entre os membros da equipe sobre a forma de codificação, sobre o estilo de código que cada um deles pode usar (lembra de várias versões de PSRs em PHP?). Nós, em nossa equipe de 7 membros, nunca discutimos sobre os códigos uns dos outros. Apenas focamos no nosso objetivo.

Não que eu não goste do operador condicional ternário, mas preciso objetar adicioná-lo e qualquer coisa do tipo Go porque não gosto da maneira de codificar se tornar um dos nossos argumentos.

Açúcar desnecessário. Acho que é um daqueles momentos em que a gente tenta trazer algo que "foi usado em outro idioma". Não sente Go.

Como observado acima, existe uma entrada de FAQ existente explicando por que a linguagem não possui o operador condicional. A questão não tem um forte apoio. Adicionar um novo recurso ao idioma nunca pode torná-lo mais simples. Uma vez que existem duas maneiras de fazer algo ( if ou ?: ), cada programador muitas vezes terá que decidir qual forma usar, o que não é uma coisa boa. Em geral, não vemos nenhum argumento novo aqui.

Portanto, este é um declínio provável . Deixando aberto por um mês para comentários finais.

Para o ponto de @ziflex , não tenho certeza de como um inline detalhado se:

if bool := operation(); bool {}

é muito diferente de um operador ternário. É uma construção que eu não desistiria com prazer, mas tem a possibilidade de sofrer a mesma complexidade de um 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
}

Isso serve apenas para ilustrar que código mal escrito em uma única linha pode se tornar tão ilegível quanto alguns dos exemplos acima. A capacidade de abusar de uma sintaxe, IMHO, não deve ser motivo suficiente para impedir que um operador significativamente utilizável seja adicionado à linguagem.

No entanto, a forma if-else faz seu trabalho. Eu acho que uma forma mais curta nem sempre é a forma mais clara.

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

Esse é um exemplo ruim, porque você omitiu parênteses, o que faz com que seu exemplo pareça extremamente ofuscado, embora possa ser compreensível.

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

é mais legível do 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)

(Mesmo que seu exemplo pudesse ser ainda mais simples, mas presumo que não seja esse o ponto:)

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

Embora ocasionalmente me encontre querendo uma condicional em outras expressões ou declarações, quase sempre sinto que seria uma perda líquida para a legibilidade.

A única coisa que me arrependo é a falta de uma maneira de ter uma inicialização condicional onde ambas as ramificações não seriam um valor zero. Eu sei que o compilador provavelmente é inteligente o suficiente para fazer com que isso não seja um desperdício, mas meu senso do que a máquina abstrata faz me diz que estou zerando um valor e, em seguida, sobrescrevendo-o imediatamente. Este é... um argumento muito fraco, na verdade.

FWIW, não acho a forma ternária dos exemplos "Example bool" mais legível, até porque, na minha tela agora, eles acabam exigindo rolagem horizontal para ver a expressão completa. Mesmo fazendo isso, eu tenho que olhar para frente e para trás com muito mais frequência para descobrir o que está fazendo.

A resposta óbvia de algumas das linguagens de script é ter instruções if como expressões que podem ser atribuídas, e eu realmente gosto muito desse design para essas linguagens, mas não acho que gostaria tanto de ir.

Suponho que há sempre:

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

... mas pensando bem, se pudéssemos simplificar isso, seria bastante útil para circunstâncias como esta. Algo que nos permite expressar "essa função é na verdade meramente notacional, não há necessidade de gerar código de função, apenas queremos uma maneira de usar expressões de retorno em um bloco interno"...

A linguagem Go nasceu para a simplicidade. Por que você quer fazer essas coisas extravagantes? Você sente que escreveu algumas linhas de código, mas isso adiciona mais complexidade e confusão ao código.
Então eu não apoio

Não é “chique”, é “simples” então se encaixa perfeitamente. É familiar porque usamos em muitos outros idiomas. É conveniente porque é uma expressão de linha única com pouca digitação. É importante ter porque é uma construção comum que usaríamos muito.

Então eu prefiro usar uma compreensão de lista como python:
a if a>1 else b
Em vez de todos os tipos de símbolos estranhos, como Rust.
Prefiro escrever mais código para expressá-lo do que usar esses símbolos estranhos para omitir o código.
O código é para as pessoas lerem.

Por favor, não adicione novas funções gramaticais para uma situação que não é comumente usada, pois você está destruindo a intenção original do Go, ou seja, simples e fácil de ler.
Eu acho que, às vezes, muitas pessoas são egoístas, por sua conveniência em determinadas situações, ou porque são mimadas pelos recursos de conveniência fornecidos por outras linguagens de programação, você tem que adicionar seus recursos favoritos em Go.
O que eu quero dizer é que Go não é a sua língua, Go tem seu próprio caminho a percorrer.
Embora você possa inventar sua própria linguagem, você pode.

@Yanwenjiepy Parece que você também se tornou um "purista" do Go que, na minha opinião, está atrapalhando o progresso.

I would rather write more code to express it than to use these strange symbols
Pode ser estranho para você, mas não símbolos estranhos para a maioria de nós que está familiarizada com qualquer linguagem comum baseada em C; C, C++, C#, Java, JavaScript, etc. Todos eles têm a expressão ternária.

Please don't add new grammar functions for a situation that is not commonly used
Declarações condicionais ternárias são realmente muito úteis e comumente usadas!

Apenas para ser exigente, isso não é uma "compreensão de lista". Uma compreensão de lista é especificamente algo como [x for y in z] (possivelmente mais condições). O uso de if/else em expressões é um recurso diferente.

Estou bastante familiarizado com operadores ternários e usei linguagens que os tinham durante a maior parte da minha vida, mas cerca de 95% dos usos que vi em outras linguagens, acho que seria um ajuste ruim para o que torna o Go agradável de trabalhar in. Go tende a evitar a densidade de informações de alguns tipos, como operadores pré-incremento/pós-incremento que podem ser usados ​​em expressões, e acho que o operador ternário tem o mesmo problema subjacente; é preciso muito espaço para pensar sobre o que ele faz.

O compilador é razoavelmente inteligente. Você pode declarar um valor, atribuí-lo condicionalmente, usá-lo uma vez e esperar que o compilador faça o mesmo que faria para uma expressão ternária.

@Yanwenjiepy Parece que você também se tornou um "purista" do Go, o que na minha opinião atrapalha o progresso.

I would rather write more code to express it than to use these strange symbols
Para a maioria de nós que está familiarizado com qualquer linguagem C comum, isso pode ser uma notação estranha, mas não estranha; C, C++, C#, Java, JavaScript, etc. Ambos têm expressões ternárias.

Please don't add new grammar functions for a situation that is not commonly used
Declarações condicionais ternárias são realmente muito convenientes e comumente usadas!
Talvez, eu mesmo tenha um sentimento semelhante. Talvez em outras questões, eu não seja um 'ir purista'. Isso é muito confuso.

@seebs Eu certamente posso respeitar essa opinião, mas para muitos de nós vindos de outras linguagens baseadas em C, "agradável de trabalhar" significa produtividade por meio de familiaridade e conveniência. Eu nunca consegui entender por que i++ em Go é menos agradável/conveniente do que i = i + 1 , especialmente quando um pós-incremento está ok em loops, por exemplo for i := 0; i < 5; i++ {...} mas não é bom como uma declaração! Purismo demais eu digo! :)

O que você está falando? i++ é perfeitamente permitido como uma declaração em Go. o que não é permitido é usá-lo em uma _expressão_, onde é avaliado tanto por um valor quanto por um efeito colateral.

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

Como alguém razoavelmente familiarizado com C, ainda assim acho que estou bem sem o operador ternário e gosto mais do código resultante. Eu parei de usá-lo tanto em C também, assim como me tornei mais consistente sobre o uso de chaves em todos os blocos, não apenas em blocos com mais de uma instrução neles.

Eu não quis dizer i++ por si só, eu quis dizer como uma expressão como fmt.Printf("%d", i++) que seria uma conveniência para alguns de nós.

Sim, é definitivamente conveniente, mas é, claramente, menos sustentável. As pessoas cometem erros com ele, as pessoas entendem mal o código usando-o. É uma fonte de bugs, e simplesmente não agrega muito valor.

Sim, se eu quiser fazer x++ , tenho que fazê-lo como sua própria declaração antes ou depois do Printf . Isso é realmente um custo, em tudo. Mas em troca, recebo:

  • De repente, não tenho x recebendo os valores errados se comentar algumas das chamadas de Printf.
  • Mudanças nas mensagens de formato não quebram a lógica do meu programa.
  • Alguém mais lendo o código (ou eu sem café suficiente) não vai simplesmente perder que o incremento está lá.

É uma troca, mas eu acho que é muito bom. Passo parte do meu tempo tentando responder a perguntas de programação para iniciantes, porque isso faz parte de como desenvolvemos comunidades de linguagem mais saudáveis. Eu gasto muito menos tempo tentando decifrar sutilezas na pontuação no código Go das pessoas do que em seu código C, e eles têm muito menos problemas causados ​​inteiramente por erros de digitação sutis.

Eu garanto a você, isso não é purismo vindo de pessoas que não entendem ou apreciam C. É uma decisão ponderada que essa linguagem parece ter muito valor por simplesmente não ser tão complicada, e isso nos deixa mais espaço para ser fazendo coisas complicadas com nossa lógica, já que não estamos gastando tanto esforço analisando o código.

Eu ouço você, mas não estou convencido de tudo isso. Por exemplo, o seguinte funciona em Go e de acordo com seu argumento, deve ser a única sintaxe permitida:

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

Mas a sintaxe mais popular/familiar também é permitida:

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

Por que você acha que é isso?

Meu argumento, de fato, não afirma que apenas o primeiro deve ser permitido. Eu gosto mais do segundo, é mais simples e fácil de ler, porque o operador de incremento é uma coisa autônoma, não um efeito colateral .

Observe que você não pode fazer:

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

Porque Go não permite que você coloque atribuições ou incrementos em expressões. E isso pode ser um inconveniente às vezes, mas a frequência com que isso não resulta em pessoas entendendo mal uma expressão e não percebendo que modifica valores é basicamente 100%, o que é bom.

Veja, isso é muito purista para mim :) De qualquer forma, meu ponto é, já que i=i+1 e i++ são permitidos em loops, eu digo também permitir uma variação ternária para aqueles que preferem a conveniência de linha única , por exemplo

``` Vá
Porto := produção ? 80: 8080

as well as the usual:

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

Se se trata de simplicidade, acho que o primeiro é mais simples.

A respeito:

Port := production++

ou

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

Ambos também são mais curtos usando o idioma C do que seriam em Go. Mas eu diria que, em ambos os casos, a possibilidade dessa complexidade existir torna o programa inteiro um pouco mais difícil de entender - agora você precisa estar atento a esses efeitos o tempo todo.

Em um nível filosófico mais fundamental: O problema é que sua solução não é só para quem prefere essa “conveniência”. Também é imposto pela força a todos os outros, para sempre. Porque todo mundo tem que ler o código de outras pessoas . Portanto, as pessoas não podem desativar um recurso como esse. Eles não podem dizer "bem, isso é mais difícil para mim manter, então não vou usá-lo" e não ter que lidar com isso. Eles estão presos com isso sendo parte de seu mundo. Sempre que lêem um código no qual qualquer outra pessoa poderia ter trabalhado, eles precisam ficar atentos a um novo conjunto de complexidades.

Na prática, essa "simplicidade" tem um custo significativo, porque na verdade não é simplicidade , é apenas encurtar a expressão. É como o if sem chave de linha única; parece que é mais simples, mas então você precisa de uma segunda linha e há uma chance diferente de zero de você esquecer de adicionar as chaves, e logo você perderá mais tempo do que teria apenas colocando as chaves lá.

Eu sei o que acontece se você escrever:

Port := production ? 80 : 8080

Alguns dias depois:

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

Mas então alguém percebe que os dois bools são uma má escolha e corrige isso:

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

e porque era apenas uma linha, e usando ?: , as pessoas sentem que torná-la mais longa é Esforço Extra, e elas não a consertam ou limpam. E agora eles têm um investimento para que seja assim.

E é assim que você acaba com as operações ?: aninhadas em 15 profundidades, que eu vi no código real, que deveria ter sido absolutamente tabelas de pesquisa.

E é assim que você termina com ?: operações aninhadas em 15 profundidades, que eu já vi no código real, que deveria ter sido absolutamente tabelas de pesquisa.

As operações "if-else" aninhadas em 15 profundidades, também deveriam ter sido absolutamente tabelas de pesquisa, no entanto.

Ah, certamente.

Mas se você tiver operações if/else aninhadas com 15 profundidades e converter em uma tabela de pesquisa, não sentirá que perdeu a "simplicidade" da solução de linha única.

O problema é que a sua solução não é só para quem prefere aquela “conveniência”. Também é imposto pela força a todos os outros, para sempre

Eu não poderia discordar mais porque a verdade é o oposto! Na verdade, é sua visão purista que impõe sua maneira de fazer e limita minha liberdade de escolher uma versão abreviada . Se você não quiser usá-lo, a escolha é sua, mas não limite a minha escolha!

Se eu puder escolher escrever um a := "freedom" abreviado em vez de var a string = "freedom" , então devo ter a liberdade e a conveniência de um trabalho ternário.

As ferramentas Go fazem um ótimo trabalho ao padronizar a formatação de código e acho que isso por si só torna bastante fácil ler o código de outras pessoas.

A conclusão para mim é que acho as tarefas ternárias mais fáceis de ler e entender porque são traduzidas mais naturalmente para o inglês. Eu acredito que é por isso que é tão popular em muitas outras línguas. Pra mim isso:

port := production ? 80 : 8080

... traduz para : "Esta produção é? se sim, a porta é 80 e se não, a porta é 8080"
(uma atribuição simples e direta, mesmo quando aninhada)

port := 8080
if production {
    port = 80
}

Isso se traduz em: "porta é 8080 (período) Ah, mas se for produção, altere a porta para 80" (segunda atribuição).

O segundo definitivamente NÃO é mais fácil de ler para mim. Nunca foi.

Se é o ? e : isso está incomodando as pessoas, eu também ficaria feliz com qualquer sintaxe alternativa de linha única.

Essa construção de linha única funciona para valores iniciais não calculados. Uma maneira de estender isso para valores iniciais calculados seria ótimo.

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

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

Infelizmente _go fmt_ destrói muitas construções úteis de linha única. Eu não uso _go fmt_ por esse motivo; o código compacto legível telescópico torna-o menos legível. Mas isso é tangencial.

Esta funcionalidade é alcançável sem um operador ternário se o Go 2 adicionar Generics

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

É claro que usar isso não seria bonito, especialmente se houvesse mais de uma chamada ternary .

Quanto à avaliação, isso pode ser uma otimização no nível do compilador.

Esta funcionalidade é alcançável sem um operador ternário se o Go 2 adicionar Generics

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

É claro que usar isso não seria bonito, especialmente se houvesse mais de uma chamada ternary .

Quanto à avaliação, isso pode ser uma otimização no nível do compilador.

Hum não, vTrue e vFalse sempre serão avaliados, quero dizer

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

causará a chamada de func1() e func2(). Nenhum compilador poderia saber que func2() não precisa ser avaliado... e ele nunca deve assumir isso de qualquer maneira, porque é um princípio fundamental que em uma chamada de função os argumentos sempre devem ser avaliados, antes da chamada da função em si. Se func2() fizer outras coisas além de retornar um valor, queremos que essas coisas sejam feitas, caso contrário, seria muito imprevisível e difícil de compreender.

(Ao contrário de um operador ternário real, onde o valor de false não deve ser avaliado por princípio.)
```

Esta funcionalidade é alcançável sem um operador ternário se o Go 2 adicionar Generics

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

É claro que usar isso não seria bonito, especialmente se houvesse mais de uma chamada ternary .
Quanto à avaliação, isso pode ser uma otimização no nível do compilador.

Hum não, vTrue e vFalse sempre serão avaliados, quero dizer

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

causará a chamada de func1() e func2(). Nenhum compilador poderia saber que func2() não precisa ser avaliado... e ele nunca deve assumir isso de qualquer maneira, porque é um princípio fundamental que em uma chamada de função os argumentos sempre devem ser avaliados, antes da chamada da função em si. Se func2() fizer outras coisas além de retornar um valor, queremos que essas coisas sejam feitas, caso contrário, seria muito imprevisível e difícil de compreender.

(Ao contrário de um operador ternário real, onde o valor de false não deve ser avaliado por princípio.)

Então a assinatura ficaria assim:

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

Eu tenho que admitir, porém, essa implementação é muito feia :(

Houve comentários adicionais desde que declaramos que isso era um provável declínio (https://github.com/golang/go/issues/33171#issuecomment-525486967), mas até onde podemos dizer nenhum deles disse nada substancialmente novo. Concordamos que há casos em que a sintaxe ?: seria conveniente, mas no geral não parece valer a pena adicionar à linguagem.

-- para @golang/proposta-review

Então a decisão é baseada em "não parece valer a pena adicionar"?
Não estou nada surpreso... Expressões ternárias destruiriam a "pureza" da linguagem, certo?

As mudanças de idioma são sempre uma troca de custo-benefício. Raramente há uma resposta inequívoca.

Não há uma explicação fácil sobre a ausência do período do operador ternário.

À primeira vista, a recusa em implementar esse recurso básico é análoga a argumentar que as bicicletas podem ser perigosas se forem rápidas e, como resultado, recusar-se a fazer uma bicicleta com marchas altas. Alguns podem se confundir na conversa sobre se os arquitetos de linguagem do Go estão certos ou errados, mas eu prefiro subir um nível de abstração e considerar se as linguagens devem combinar preocupações de recursos com preocupações de manutenção:

Se o recurso X pode ser abusado e resultar em um ninho de ratos de código, isso é motivo suficiente para o recurso X ser excluído da linguagem?

Eu diria que pode ser, mas não por si só: deve ser pesado contra Demanda e Dificuldade. Se um recurso é de alta demanda e fácil de implementar, seria melhor desacoplar as preocupações implementando o recurso e apresentando uma maneira de desativá-lo – e até mesmo desativá-lo por padrão.

Na verdade, se a demanda for alta o suficiente, até mesmo a dificuldade é uma má razão para recusá-la. Considere a sintaxe class em Javascript (ES2015): Os arquitetos da linguagem realmente não queriam adicionar o recurso e, na verdade, foi um pouco trabalhoso adicionar a sintaxe, mas a demanda foi incrivelmente alta. O que eles fizeram? Eles adicionaram a sintaxe, sabendo muito bem que qualquer organização que não quisesse o recurso poderia facilmente não permitir a sintaxe no nível de linting.

Esta é a resolução adequada para o operador ternário, dada a demanda. Desacoplar essas preocupações é apropriado e resolvê-las de uma maneira mais configurável faria mais sentido. O mesmo deve acontecer para coisas como erros de "variável não usada" que transformam problemas de linting em crises de "parar as impressoras, você precisa corrigir isso antes que o programa seja executado". (Sim, eu sei que existe uma solução _ , mas ainda é o caso de que isso deve ser configurável)

É um erro pensar que uma linguagem deve ser o produto de um pequeno número de arquitetos que sabem melhor sem uma conexão profunda com aqueles que estão realmente usando a linguagem. O pedido de dados para provar que os arquitetos da linguagem estão errados é admirável, mas essa análise é desnecessária. Basta olhar para o tamanho deste segmento: há demanda.

Infelizmente, ignorar a demanda é o que leva a produtos concorrentes: nesse caso, é assim que os idiomas são bifurcados. Você quer se bifurcar? (Para ser claro: esta é uma previsão, não uma ameaça.
Eu definitivamente não estou bifurcando um idioma.)

@dash Este problema está encerrado e não vou discutir isso, mas gostaria de corrigir o que acredito ser uma deturpação. Você está insinuando que Go é uma linguagem que é "... o produto de um pequeno número de arquitetos que sabem melhor _sem uma conexão profunda com aqueles que estão realmente usando a linguagem_." Isso certamente não é verdade. Todos na equipe Go, e certamente os "arquitetos" estão escrevendo código Go todos os dias, e têm escrito uma quantidade substancial desde 2007. Também interagimos com outros usuários Go quase diariamente. Temos absolutamente uma conexão profunda com aqueles que realmente usam a língua, que somos nós - entre muitos outros.

Não sou um dos arquitetos e uso muito a linguagem, e encontro regularmente situações em que quase certamente usaria um operador ternário se estivesse disponível. E então eu leio meu código mais tarde, e penso sobre isso, e fico feliz que não esteja lá. YMMV.

Eu não acho que tornar as coisas assim, ou os avisos de variáveis ​​não utilizadas, "configuráveis" tornaria minha vida como desenvolvedor mais fácil; Acho que isso tornaria minha vida como desenvolvedor mais difícil.

Também não sou um dos arquitetos, e também uso muito a linguagem, e encontro regularmente situações em que quase certamente usaria um operador ternário se estivesse disponível. E então eu li meu código mais tarde, e amaldiçoo as poucas pessoas que nos negam esse recurso útil!

O mesmo aqui, eu uso Go todos os dias e precisaria dele todos os dias e estou convencido de que tornaria meu código mais claro e ainda mais robusto.

A propósito, na proposta de "Reword FAQ answer"

usando operador ternário em vez de instrução condicional (não expressão) para brevidade;

"Brevity" é mencionado como se fosse uma coisa ruim. A brevidade ajuda a legibilidade. A ideia de um código legível é que ele é "direto ao ponto" do que realmente faz. Não é como afetar 8080 ou -1 na porta e depois 80 mais tarde no código porque isso é produção.

Acho que há pouca chance agora de Go obter um operador ternário, não apenas porque a equipe Go sempre argumentou contra isso, mas porque (a julgar pela votação dos emojis) cerca de 60% da comunidade também é contra.

No entanto, se o Go eventualmente obtiver genéricos, acho que a equipe deve considerar seriamente a adição de uma função ternária à biblioteca padrão, apesar de não haver curto-circuito, exceto possivelmente por meio de otimização do compilador.

Se eles não fizerem isso, os 40% que são a favor de algum tipo de operador/função termary (inclusive eu) escreverão imediatamente seus próprios. Isso criará um pesadelo de legibilidade e manutenção porque nomes diferentes serão escolhidos (Cond, Iff, Iif, Pick, Choose, Tern etc.) e estarão em pacotes com nomes diferentes.

Se, em vez disso, for adicionado à biblioteca padrão, essa fragmentação não ocorrerá, pois todos a favor usarão a versão padrão e aqueles que não gostarem saberão pelo menos o que ela faz.

func ifThen(condition bool, ifTrue,ifelse interface{}) interface{}{
se condição {
retornar se Verdadeiro
} outro {
devolva se
}
}

Parece-me que esta discussão sobre o operador ternário em alguns casos se resume a uma "solução para um problema diferente".

A falta de valores padrão em funções resulta em pessoas escrevendo código como:

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

com pessoas querendo simplesmente isso como:

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

Ou

func DoDesign( elementType string = "Whatever" )

Assim, um operador ternário tenta resolver um problema relacionado a outro problema. Embora a versão Go mais padrão seja de fato mais legível, ela fica menos legível quando você está lidando com 4 ou 5 delas seguidas.

Também é preciso questionar se a legibilidade é atendida quando as pessoas começam a construir cada vez mais suas próprias "soluções" como mostra @ArnoldoR . Um dos problemas que atormentam o Javascript é o crescimento de "soluções" para funcionalidades ausentes, o que resultou em alguns projetos importando pacotes NPM para a esquerda e para a direita. Pacotes populares como o SqlX são mais um sinal de recursos ausentes no Go.

Legibilidade é uma coisa. Mas ter que escrever às vezes 20 linhas que são mais ou menos podem estar contidas em 5 linhas. Ele se acumula em qualquer projeto.

Se o problema for que o ternário pode ser mal utilizado para criar código ilegível, especialmente operadores ternários aninhados, coloque um limite nele. Acho que a maioria das pessoas neste tópico não terá problemas se um operador ternário estiver limitado a apenas "um nível" e o compilador impedir você de operadores de nível profundo.

Sabemos que as pessoas vão abusar dos genéricos para implementá-los de qualquer maneira. Então por que não fornecer uma versão oficial, então você pode limitar o abuso dela. Mas essas funções selvagens, como vemos acima, só se tornarão mais populares com o tempo, como vimos na comunidade Javascript. E quando o gênio sai da garrafa, não há mais como controlá-lo.

Esta página foi útil?
0 / 5 - 0 avaliações