Por que o tipo vai após o nome da variável nas linguagens de programação modernas?

38

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

Por que x: int = 42 e não int x = 42 ?
Este último não é mais legível que o anterior?
É apenas uma tendência ou há alguma razão realmente significativa por trás dessa solução?

    
por Andre Polykanine 19.04.2016 / 20:39
fonte

4 respostas

47

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

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

Esse argumento fica especialmente strong quando você considera todos os modificadores de tipo opcionais que uma linguagem "não-moderna" como C ++ possui, como * para ponteiros, & para referências, const , volatile e em breve. Depois de lançar vírgulas para várias declarações, você começa a obter algumas ambigüidades realmente estranhas, como int* a, b; não tornando b um ponteiro.

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

    
por 19.04.2016 / 20:53
fonte
21

Why in nearly all modern programming languages (Go, Rust, Kotlin, Swift, Scala, Nim, even Python last version) types always come after the variable declaration, and not before?

Sua premissa é falha em duas frentes:

  • Existem linguagens de programação new-ish que possuem o tipo antes do identificador. C♯, D ou Ceilão, por exemplo.
  • Ter o tipo após o identificador não é um fenômeno novo, ele remonta a pelo menos Pascal (projetado em 1968-1969, lançado em 1970), mas na verdade foi usado na teoria matemática de 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, por isso não é de surpreender que esse estilo de declarações de tipos se mantivesse popular.

Why x: int = 42 and not int x = 42? Is the latter not more readable than the former?

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

Is it just a trend or are there any really meaningful reasons behind such a solution?

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 para fora se você quiser que ele seja inferido. Se suas declarações se parecem com isso:

val i: Int = 10

Então é trivial deixar de fora o tipo e tê-lo inferido assim:

val i = 10

Considerando que o tipo vem antes do identificador como este:

Int i = 10

Em seguida, começa a ficar difícil para o analisador distinguir uma expressão de uma declaração:

i = 10

A solução que os projetistas de linguagens normalmente criam é introduzir uma palavra-chave "não quero escrever um tipo" que tenha que ser escrita em vez do tipo:

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

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

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

Os projetistas de várias das linguagens mencionadas já se debruçaram sobre este assunto:

  • Vá a FAQ (veja também: Sintaxe de declaração de Go :

    Why are declarations backwards?

    They're only backwards if you're used to C. In C, the notion is that a variable is declared like an expression denoting its type, which is a nice idea, but the type and expression grammars don't mix very well and the results can be confusing; consider function pointers. Go mostly separates expression and type syntax and that simplifies things (using prefix * for pointers is an exception that proves the rule). In C, the declaration

    int* a, b;
    

    declares a to be a pointer but not b; in Go

    var a, b *int
    

    declares both to be pointers. This is clearer and more regular. Also, the := short declaration form argues that a full variable declaration should present the same order as := so

    var a uint64 = 1
    

    has the same effect as

    a := uint64(1)
    

    Parsing is also simplified by having a distinct grammar for types that is not just the expression grammar; keywords such as func and chan keep things clear.

  • Perguntas frequentes sobre o Kotlin :

    Why have type declarations on the right?

    We believe it makes the code more readable. Besides, it enables some nice syntactic features. For instance, it is easy to leave type annotations out. Scala has also proven pretty well this is not a problem.

  • Programação no Scala :

    The major deviation from Java concerns the syntax for type annotations—it's "variable: Type" instead of "Type variable" in Java. Scala's postfix type syntax resembles Pascal, Modula-2, or Eiffel. The main reason for this deviation has to do with type inference, which often lets you omit the type of a variable or the return type of a method. Using the "variable: Type" syntax this is easy—just leave out the colon and the type. But in C-style "Type variable" syntax you cannot simply leave off the type—there would be no marker to start the definition anymore. You'd need some alternative keyword to be a placeholder for a missing type (C♯ 3.0, which does some type inference, uses var for this purpose). Such an alternative keyword feels more ad-hoc and less regular than Scala's approach.

Nota: os designers do Ceylon também documentaram porque usam a sintaxe do tipo de prefixo :

Prefix instead of postfix type annotations

Why do you follow C and Java in putting type annotations first, instead of Pascal and ML in putting them after the declaration name?

     

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 nós simplesmente não entendemos como alguém poderia pensar de outra maneira!

Pessoalmente, considero o argumento deles muito menos convincente do que os outros.     
por 20.04.2016 / 03:19
fonte
2

Pascal também o faz, e não é um novo idioma. Era uma linguagem acadêmica, porém, projetada a partir 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 aula como o modelo de realidade que é, faz sentido colocar os nomes de suas entidades em primeiro lugar e sua implementação técnica por último.

C # e java stem de C então eles tiveram que respeitar o "prior art", então não confundir o programador experiente.

    
por 19.04.2016 / 20:57
fonte
1

Why x: int = 42 and not int x = 42? Is the latter not more readable than the former?

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 em C, mas não faz o que parece intuitivamente. Parece que estamos declarando duas variáveis do tipo int* , mas na verdade estamos declarando um int* e um 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.

    
por 19.04.2016 / 20:45
fonte