Por que o tipo segue o nome da variável nas linguagens de programação modernas?


57

Por que quase todos os tipos de linguagens de programação modernas (Go, Rust, Kotlin, Swift, Scala, Nim e até Python) sempre vêm após o nome da variável na declaração da variável, e não antes?

Por que x: int = 42e não int x = 42?
O último não é mais legível que o primeiro?
É apenas uma tendência ou existem razões realmente significativas por trás dessa solução?



3
Sim, isso é uma bobagem, tudo bem. Embora se refira especificamente ao idioma Kotlin, a pergunta (e suas respostas) se aplica a todos os idiomas que têm essa prática.
— 21716 Robert Harvey


2
Desculpe pessoal, eu vi a pergunta sobre o Kotlin quando pesquisava no Google, mas pedi novamente para não obter respostas como "Porque a equipe de desenvolvimento do Kotlin (Go, Rust, ...) decidiu assim". Eu estava interessado em uma resposta mais comum: por que isso se torna uma espécie de epidemia?
— Andre Polykanine

2
Hihi, então Delphi, que é (Object) Pascal, e tem tipos após nomes de variáveis ​​desde o seu início, desde tempos remotos na idade das trevas do século anterior, é moderno novamente;) Btw, a legibilidade é muito afetada pelo que você é costumava. Vindo de Delphi, C # com seu tipo antes do nome var, me fez bater a cabeça por algum tempo.
— Marjan Venema

Respostas:


64

Todos os idiomas que você mencionou suportam inferência de tipo , o que significa que o tipo é uma parte opcional da declaração nesses idiomas, porque são inteligentes o suficiente para preenchê-los quando você fornece uma expressão de inicialização que tem um tipo facilmente determinado.

Isso importa porque colocar as partes opcionais de uma expressão mais à direita reduz a ambiguidade da análise e aumenta a consistência entre as expressões que usam essa parte e as que não o fazem. É mais simples analisar uma declaração quando você sabe que a varpalavra - chave e o nome da variável são obrigatórios antes de acessar o material opcional. Em teoria, todas as coisas que facilitam a análise de computadores também devem melhorar a legibilidade geral para os seres humanos, mas isso é muito mais discutível.

Este argumento fica especialmente forte quando se consideram todos os modificadores opcionais tipo que uma língua "não-moderno", como C ++ tem, como *para ponteiros, &para referências, const, volatilee assim por diante. Depois de inserir vírgulas para várias declarações, você começa a obter algumas ambiguidades realmente estranhas, como int* a, b;não fazer bum ponteiro.

Até o C ++ agora suporta declarações "digite à direita" na forma de auto x = int{ 4 };, e possui algumas vantagens .


2
+1 por reconhecer a existência de palavras-chave obrigatórias como varessa fornece a maior parte da simplificação de análise.
— precisa saber é o seguinte

2
A existência da varpalavra - chave não anula o objetivo da notação nome-primeiro? Eu não vejo a vantagem de escrever var userInput: String = getUserInput()sobre String userInput = getUserInput(). A vantagem seria relevante apenas se userInput = getUserInput()também for permitida, mas isso implicaria a declaração implícita de uma variável com escopo definido, o que eu acho bastante repugnante.
— Kdb

2
@kdb em linguagens como C # e C ++ seria var userInput = getUserInput()ou auto userInput = getUserInput(), o compilador sabe getUserInput()retornos Stringpara que você não precise especificá-lo com a palavra-chave dedução de tipo. userInput = getUserInput()é claro que usar antes de declarar.
— Wes Toleman 24/04

32

Por que em quase todas as linguagens de programação modernas (Go, Rust, Kotlin, Swift, Scala, Nim e até a última versão do Python) os tipos sempre vêm após a declaração da variável, e não antes?

Sua premissa é falha em duas frentes:

  • Existem novas linguagens de programação que têm o tipo antes do identificador. C♯, D ou Ceilão, por exemplo.
  • Tendo o tipo após o identificador não é um fenômeno novo, ele remonta a pelo menos Pascal (projetado 1968-1969, lançado em 1970), mas na verdade foi usado na teoria matemática dos tipos, que começa em 1902. Também foi usado em ML (1973), CLU (1974), Hope (1970), Modula-2 (1977-1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985), Oberon (1986), Modula-3 (1986-1988) e Haskell (1989).

Pascal, ML, CLU, Modula-2 e Miranda eram linguagens muito influentes; portanto, não é de surpreender que esse estilo de declaração de tipo permanecesse popular.

Por que x: int = 42e não int x = 42? O último não é mais legível que o primeiro?

A legibilidade é uma questão de familiaridade. Pessoalmente, acho o chinês ilegível, mas aparentemente o povo chinês não. Tendo aprendido Pascal na escola, brincado com Eiffel, F♯, Haskell e Scala, e tendo olhado para TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml,…, parece completamente natural para mim .

É apenas uma tendência ou existem razões realmente significativas por trás dessa solução?

Se é uma tendência, é bastante persistente: como mencionei, remonta a cem anos em matemática.

Uma das principais vantagens de ter o tipo após o identificador é que é fácil deixar o tipo de fora, se você desejar que ele seja deduzido. Se suas declarações forem assim:

val i: Int = 10

Então é trivial deixar de fora o tipo e inferi-lo assim:

val i = 10

Considerando que, se o tipo vier antes do identificador, desta forma:

Int i = 10

Então, fica difícil para o analisador distinguir uma expressão de uma declaração:

i = 10

A solução que os designers de linguagem geralmente apresentam é a introdução de uma palavra-chave "Não quero escrever um tipo" que deve ser escrita em vez do tipo:

var  i = 10; // C♯
auto i = 10; // C++

Mas isso realmente não faz muito sentido: você basicamente precisa escrever explicitamente um tipo que diz que você não escreve um tipo. Hã? Seria muito mais fácil e sensato deixar de fora, mas isso torna a gramática muito mais complexa.

(E não vamos nem falar sobre os tipos de ponteiros de função em C.)

Os projetistas de várias das línguas mencionadas anteriormente abordaram esse assunto:

  • Go FAQ (consulte também: Sintaxe da declaração de Go ):

    Por que as declarações são invertidas?

    Eles estão apenas ao contrário se você estiver acostumado a C. Em C, a noção é que uma variável é declarada como uma expressão que indica seu tipo, o que é uma boa ideia, mas as gramáticas de tipo e expressão não se misturam muito bem e os resultados podem ser confusos; considere ponteiros de função. Go principalmente separa a expressão e o tipo de sintaxe e isso simplifica as coisas (o uso do prefixo *para ponteiros é uma exceção que comprova a regra). Em C, a declaração

    int* a, b;
    

    declara aser um ponteiro, mas não b; em Go

    var a, b *int
    

    declara que ambos são ponteiros. Isso é mais claro e mais regular. Além disso, o :=formulário de declaração abreviada argumenta que uma declaração variável completa deve apresentar a mesma ordem que :=a

    var a uint64 = 1
    

    tem o mesmo efeito que

    a := uint64(1)
    

    A análise também é simplificada por ter uma gramática distinta para tipos que não é apenas a gramática da expressão; palavras-chave como funce chanmantenha as coisas claras.

  • Perguntas frequentes sobre o Kotlin :

    Por que ter declarações de tipo à direita?

    Acreditamos que torna o código mais legível. Além disso, ele permite alguns recursos sintáticos agradáveis. Por exemplo, é fácil deixar anotações de tipo. Scala também provou muito bem que isso não é um problema.

  • Programação em Scala :

    O principal desvio do Java diz respeito à sintaxe das anotações de tipo - é " variable: Type" em vez de " Type variable" em Java. A sintaxe do tipo postfix de Scala se assemelha a Pascal, Modula-2 ou Eiffel. O principal motivo desse desvio está relacionado à inferência de tipo, que geralmente permite omitir o tipo de uma variável ou o tipo de retorno de um método. Usar a variable: Typesintaxe " " é fácil - deixe de fora os dois pontos e o tipo. Mas na Type variablesintaxe " " do estilo C, você não pode simplesmente deixar de fora o tipo - não haveria mais marcador para iniciar a definição. Você precisaria de alguma palavra-chave alternativa para ser um espaço reservado para um tipo ausente (o C♯ 3.0, que faz alguma inferência de tipo, usa varpara esse fim). Essa palavra-chave alternativa parece mais ad-hoc e menos regular do que a abordagem da Scala.

Nota: os designers do Ceilão também documentaram por que usam a sintaxe do tipo de prefixo :

Prefixo em vez de anotações do tipo postfix

Por que você segue C e Java colocando as anotações de tipo primeiro, em vez de Pascal e ML colocando-as após o nome da declaração?

Porque pensamos isso:

shared Float e = ....
shared Float log(Float b, Float x) { ... }

É simplesmente muito mais fácil de ler do que isso:

shared value e: Float = .... 
shared function log(b: Float, x: Float): Float { ... }

E simplesmente não entendemos como alguém poderia pensar o contrário!

Pessoalmente, acho o "argumento" deles muito menos convincente do que os outros.


4
Parece que a capacidade de deixar de fora o tipo e ainda temos simples análise é concedido pela adição de var, auto, letou semelhantes, não por que lado da variável a declaração de tipo está ligado. var Int x = 5ou até Int var x = 5poderiam ser reduzidos para var x = 5.
— precisa saber é o seguinte

5
Embora eu goste da leitura igualmente fácil quando você se acostumar e o argumento do tipo opcional for forte, gostaria de salientar que o IDE não é capaz de propor automaticamente nomes de variáveis ​​com base no tipo. Sinto falta disso quando escrevo TypeScript.
— 23618 Pierre Pierre

"Your premise is flawed on two fronts" Todos esses são mais recentes que C # ou D.
— Det

3
"Mas isso realmente não faz muito sentido: você basicamente tem que escrever explicitamente um tipo que diz que você não escreve um tipo". Bem, a versão digitar à direita é exatamente a mesma no seu exemplo ... Também discordo que não faz sentido. Faz exatamente tanto sentido quanto funcem Go.
— Sopel

4

Pascal faz isso também por sinal, e não é um novo idioma. Era uma linguagem acadêmica, projetada do zero.

Eu diria que é semanticamente mais claro começar com o nome da variável. O tipo é apenas um detalhe técnico. Se você quiser ler sua classe como o modelo de realidade atual, faz sentido colocar os nomes de suas entidades em primeiro lugar e sua implementação técnica por último.

C # e java derivam de C, então eles tiveram que respeitar a "arte anterior", para não confundir o programador experiente.


3
Ada também faz dessa maneira, mas certamente não é uma "linguagem acadêmica".
— 1911 John R. Strohm

Ada fez isso porque ele plagiou fortemente a partir de Pascal e Modula 2.
— Gort the Robot

2
@StevenBurnap, uma pesquisa rápida revela que o primeiro livro de Wirth sobre o Modula-2 foi lançado em meados de 1982. Ada estava em desenvolvimento vários anos antes disso (o DoD1 estava em 1977, pelo que me lembro), e padronizou, como um padrão ANSI e um MIL-STD, em 1983. Todas as quatro equipes que apresentaram candidatos ao Ada fizeram o PASCAL como pontos de partida. (Laboratórios Bell foi convidado pelo DoD para enviar uma linguagem candidato baseado em C. Eles objetou, dizendo que C não foi seguida, e não iria nunca ser robusto o suficiente para o software de missão crítica DoD.)
— John R. Strohm

Eu devo estar me lembrando. Eu pensei que Wirth estivesse envolvido em Ada.
— Gort the Robot

Pascal começou uma linguagem acadêmica, mas até o final da década de 90 ele estava à beira de ser a linguagem para escrever aplicativos do Windows em via Delphi, que é até Borland pulou o tubarão com preços e deixou a porta aberta para Java e C # para venha e roube o prêmio. Você ainda verá uma tonelada de Delphi no mundo dos aplicativos Windows herdados.
— Shayne

1

Por que x: int = 42e não int x = 42? O último não é mais legível que o primeiro?

Em um exemplo tão simples, não há muita diferença, mas vamos torná-lo um pouco mais complexo: int* a, b;

Esta é uma declaração válida e real em C, mas não faz o que parece intuitivamente que deveria fazer. Parece que estamos declarando duas variáveis ​​do tipo int*, mas na verdade estamos declarando uma int*e uma int.

Se o seu idioma colocar o tipo após o (s) nome (s) da variável em suas declarações, é impossível encontrar esse problema.


4
Não sei por que mover a declaração de tipo depois afetaria isso. São x, y *int;dois *intou um *inte um int? Fazendo int*ou *intum símbolo em vez de dois iria resolvê-lo, ou usando um elemento sintática adicional, como o :que poderia trabalhar em qualquer direção.
— precisa saber é o seguinte

@ 8bittree Todos os idiomas que eu conheço e que usam declarações de primeiro nome usam um token adicional, como :ou as, entre o nome e o tipo.
— Mason Wheeler


2
E em Go, x, y *intsignifica "declarar duas variáveis, x e y, com o tipo ponteiro para int".
— Vatine

10
O C # usa a sintaxe do prefixo, mas trata int[] x, ycomo duas matrizes. É apenas a tola sintaxe C que trata esses modificadores como operadores unários na variável (e uma mistura de prefixo e postfix, nada menos) que causa problemas.
— CodesInChaos
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.