Pourquoi le type suit-il le nom de variable dans les langages de programmation modernes?

57

Pourquoi, dans presque tous les langages de programmation modernes (Go, Rust, Kotlin, Swift, Scala, Nim et même dernière version de Python), les types viennent toujours après le nom de la variable dans la déclaration de la variable et pas avant?

Pourquoi x: int = 42et pas int x = 42?
Ce dernier n'est-il pas plus lisible que l'ancien?
Est-ce juste une tendance ou y a-t-il des raisons vraiment significatives derrière cette solution?

André Polykanine
la source
3
Oui, c'est une dupe, d'accord. Bien qu'elle se réfère spécifiquement à la langue kotlin, la question (et ses réponses) s'applique à toutes les langues ayant cette pratique.
Robert Harvey
2
Désolé les gars, j'ai vu la question à propos de Kotlin lors de la recherche sur Google, mais je l'ai redemandée pour ne pas obtenir de réponses telles que "Parce que l'équipe de développement de Kotlin (Go, Rust, ...) a décidé comme ça". Je voulais une réponse plus commune: pourquoi cela devient-il une sorte d'épidémie?
André Polykanine le
2
Hihi, donc Delphi, qui est (Object) Pascal, et qui a des types après les noms de variables depuis sa création, il y a bien longtemps, à l'époque sombre du siècle précédent, est à nouveau moderne;) Btw, la lisibilité dépend beaucoup de ce que vous êtes habitué. Venant de Delphi, C # avec son type précédant le nom de la variable, me faisait frapper à la tête depuis un certain temps.
Marjan Venema

Réponses:

64

Toutes les langues que vous avez mentionnées prennent en charge l' inférence de type , ce qui signifie que le type est une partie facultative de la déclaration dans ces langues, car elles sont assez intelligentes pour le remplir elles-mêmes lorsque vous fournissez une expression d'initialisation ayant un type facilement déterminé.

Cela est important, car placer les parties facultatives d'une expression plus à droite réduit l'analyse d'ambiguïté et augmente la cohérence entre les expressions qui utilisent cette partie et celles qui ne le font pas. Il est simplement plus simple d'analyser une déclaration lorsque vous connaissez le varmot - clé et le nom de la variable sont obligatoires avant de passer aux éléments facultatifs. En théorie, toutes les choses qui facilitent l'analyse des ordinateurs devraient également améliorer la lisibilité pour les humains, mais c'est beaucoup plus discutable.

Cet argument se particulièrement fort lorsque l' on considère tous les modificateurs de type option qu'une langue « non-moderne » comme C ++ a, comme *pour les pointeurs, &les références, const, volatileet ainsi de suite. Une fois que vous insérez des virgules pour plusieurs déclarations, vous commencez à avoir des ambiguïtés vraiment étranges, comme int* a, b;ne pas faire de bpointeur.

Même le C ++ supporte maintenant les déclarations "dactylographiées à droite" sous la forme de auto x = int{ 4 };, et il présente certains avantages .

Ixrec
la source
2
+1 pour reconnaître l'existence de mots-clés obligatoires tels varque ceux-ci fournissent la majorité de la simplification de l'analyse.
8bittree
2
L'existence du varmot - clé ne va-t-elle pas à l'encontre de l' objectif de la notation nom-premier? Je ne vois pas l'avantage d'écrire var userInput: String = getUserInput()sur String userInput = getUserInput(). L'avantage ne serait pertinent que s'il userInput = getUserInput()est également autorisé, mais cela impliquerait la déclaration implicite d'une variable étendue, ce que je trouve plutôt abominable.
kdb
2
@kdb dans des langages tels que C # et C ++ , var userInput = getUserInput()ou bien auto userInput = getUserInput(), le compilateur sait que les getUserInput()retours reviennent String. Vous n'avez donc pas à le spécifier avec le mot clé type deduction. userInput = getUserInput()est bien sûr à utiliser avant de déclarer.
Wes Toleman
32

Pourquoi dans presque tous les langages de programmation modernes (Go, Rust, Kotlin, Swift, Scala, Nim et même dernière version de Python), les types viennent toujours après la déclaration de variable, et pas avant?

Votre prémisse est défectueuse sur deux fronts:

  • Il existe de nouveaux langages de programmation qui ont le type avant l'identifiant. C♯, D ou Ceylan, par exemple.
  • Le type après l’identifiant n’est pas un phénomène nouveau, il remonte au moins à Pascal (conçu de 1968 à 1969, publié en 1970), mais a en fait été utilisé dans la théorie des types mathématiques, qui débute vers 1902. Il a également été utilisé dans ML (1973), CLU (1974), Hope (1970), Modula-2 (1977-1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985), Oberon (1986), Modula-3 (1986-1988) et Haskell (1989).

Pascal, ML, CLU, Modula-2 et Miranda étaient tous des langages très influents. Il n’est donc pas étonnant que ce style de déclaration de caractères soit resté populaire.

Pourquoi x: int = 42et pas int x = 42? Ce dernier n'est-il pas plus lisible que l'ancien?

La lisibilité est une question de familiarité. Personnellement, je trouve le chinois illisible, mais apparemment pas les Chinois. Avoir appris Pascal à l'école, côtoyé Eiffel, F♯, Haskell et Scala, et avoir regardé TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml,…, cela me paraissait tout à fait naturel .

Est-ce juste une tendance ou y a-t-il des raisons vraiment significatives derrière une telle solution?

Si c'est une tendance, c'est assez persistant: comme je l'ai mentionné, cela remonte à cent ans en mathématiques.

L'un des principaux avantages d'avoir le type après l'identificateur est qu'il est facile de laisser le type en dehors si vous souhaitez l'inférer. Si vos déclarations ressemblent à ceci:

val i: Int = 10

Ensuite, il est trivial de laisser le type et de l'inférer comme ceci:

val i = 10

Considérant que si le type précède l'identifiant comme ceci:

Int i = 10

Ensuite, l'analyseur commence à avoir du mal à distinguer une expression d'une déclaration:

i = 10

La solution généralement proposée par les concepteurs de langage consiste à introduire un mot clé "je ne veux pas écrire de type" qui doit être écrit à la place du type:

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

Mais cela n'a pas vraiment de sens: vous devez fondamentalement écrire explicitement un type qui dit que vous n'écrivez pas de type. Hein? Il serait beaucoup plus facile et plus judicieux de simplement laisser tomber, mais cela rend la grammaire beaucoup plus complexe.

(Et ne parlons même pas des types de pointeurs de fonction en C.)

Les concepteurs de plusieurs des langues susmentionnées ont pesé à ce sujet:

  • Go FAQ (voir aussi: Syntaxe de la déclaration de Go ):

    Pourquoi les déclarations sont-elles à l'envers?

    Ils sont seulement à l'envers si vous êtes habitué à C. En C, la notion est qu'une variable est déclarée comme une expression désignant son type, ce qui est une bonne idée, mais les grammaires de type et d'expression ne se mélangent pas très bien et les résultats peuvent être déroutants; considérez les pointeurs de fonction. Go sépare principalement l'expression et la syntaxe de type et cela simplifie les choses (l'utilisation du préfixe *pour les pointeurs est une exception qui prouve la règle). En C, la déclaration

    int* a, b;
    

    déclare aêtre un pointeur mais pas b; dans Go

    var a, b *int
    

    déclare que les deux sont des pointeurs. C'est plus clair et plus régulier. En outre, la :=forme courte déclaration soutient qu'une déclaration variable complète devrait présenter le même ordre que :=si

    var a uint64 = 1
    

    a le même effet que

    a := uint64(1)
    

    L'analyse syntaxique est également simplifiée par une grammaire distincte pour les types qui n'est pas simplement la grammaire d'expression; mots-clés tels que funcet changarder les choses claires.

  • FAQ Kotlin :

    Pourquoi avoir des déclarations de type à droite?

    Nous pensons que cela rend le code plus lisible. En outre, il permet quelques fonctionnalités syntaxiques intéressantes. Par exemple, il est facile d’omettre les annotations de type. Scala a également prouvé que ce n’est pas un problème.

  • Programmation en Scala :

    L’écart majeur par rapport à Java concerne la syntaxe des annotations de type - c’est " variable: Type" au lieu de " Type variable" en Java. La syntaxe de type postfixe de Scala ressemble à Pascal, Modula-2 ou Eiffel. La principale raison de cet écart est liée à l'inférence de type, qui vous permet souvent d'omettre le type d'une variable ou le type de retour d'une méthode. variable: TypeIl est facile d’ utiliser la syntaxe " " - il suffit d’omettre le côlon et le type. Mais dans la Type variablesyntaxe " " de style C, vous ne pouvez pas simplement laisser le type, il n’y aurait plus de marqueur pour commencer la définition. Vous auriez besoin d'un mot-clé alternatif comme espace réservé pour un type manquant (C♯ 3.0, qui effectue une inférence de type, utilise varà cette fin). Un tel mot clé alternatif semble plus ponctuel et moins régulier que l'approche de Scala.

Remarque: les concepteurs de Ceylan ont également expliqué pourquoi ils utilisaient la syntaxe de type préfixe :

Préfixe au lieu d'annotations de type postfix

Pourquoi suivez-vous C et Java pour placer les annotations de type en premier lieu, au lieu de Pascal et ML pour les placer après le nom de la déclaration?

Parce que nous pensons ceci:

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

Est tout simplement beaucoup plus facile à lire que cela:

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

Et nous ne comprenons tout simplement pas comment quiconque pourrait penser autrement!

Personnellement, je trouve leur "argument" beaucoup moins convaincant que les autres.

Jörg W Mittag
la source
4
Semble la possibilité de laisser le type et ont encore l' analyse syntaxique simple est accordée par l'ajout de var, auto, letou similaires, non par quel côté de la variable la déclaration de type est activée. var Int x = 5ou même les Int var x = 5deux pourraient être raccourcis à var x = 5.
8bittree
5
Bien que je le trouve tout aussi lisible une fois que vous vous y êtes habitué et que l'argument de type facultatif soit puissant, je voudrais souligner que l'EDI n'est pas en mesure de proposer automatiquement des noms de variable basés sur le type. Cela me manque quand j'écris en TypeScript.
Pierre Henry
"Your premise is flawed on two fronts" Tous sont plus récents que C # ou D.
Det
3
"Mais cela n'a pas vraiment de sens: vous devez fondamentalement écrire explicitement un type qui dit que vous n'écrivez pas de type." Eh bien, la version dactylographiée à droite est exactement la même dans votre exemple ... De plus, je ne suis pas d'accord pour dire que cela n'a pas de sens. Cela a tout autant de sens que funcdans Go.
Sopel
4

Pascal le fait aussi, d'ailleurs, et n'est pas un nouveau langage. C'était un langage académique cependant, conçu à partir de zéro.

Je dirais qu'il est sémantiquement plus clair de commencer par le nom de la variable. Le type est juste un détail technique. Si vous voulez lire votre classe comme un modèle de réalité, il est logique de mettre en premier le nom de vos entités et leur mise en œuvre technique.

C # et java étant issus de C, ils ont donc dû respecter "l'état de la technique" afin de ne pas confondre le programmeur expérimenté.

Martin Maat
la source
3
Ada le fait aussi de cette façon, mais Ada n'est certainement pas une "langue académique".
John R. Strohm le
Ada l'a fait parce qu'il a énormément profité de Pascal et Modula 2.
Gort the Robot
2
@StevenBurnap, une recherche rapide révèle que le premier livre de Wirth sur Modula-2 a été publié au milieu de 1982. Ada était en développement plusieurs années auparavant (DoD1 en 1977 ou à peu près, si je me souviens bien), et normalisé en tant que norme ANSI et MIL-STD en 1983. Les quatre équipes qui ont présenté des candidats à Ada ont pris PASCAL. comme points de départ. (Bell Labs a été invité par le DoD à soumettre une langue candidate basée sur C. Ils hésitèrent, en disant que C était pas alors et ne jamais être assez robuste pour la mission des logiciels critiques DoD.)
John R. Strohm
Je dois me souvenir mal. Je pensais que Wirth avait été impliqué dans Ada.
Gort le robot
Pascal a commencé un langage académique, mais à la fin des années 90, il était sur le point de devenir le langage pour écrire des applications Windows via Delphi, c'est-à-dire jusqu'à ce que Borland saute le pas au requin avec la tarification et laisse la porte ouverte à Java et C #. venez voler le prix. Vous verrez toujours une tonne de Delphi dans le monde des applications Windows traditionnelles.
Shayne le
1

Pourquoi x: int = 42et pas int x = 42? Ce dernier n'est-il pas plus lisible que l'ancien?

Dans un exemple aussi simple, il n'y a pas beaucoup de différence, mais rendons-le un peu plus complexe: int* a, b;

C'est une déclaration valide en C, mais elle ne fait pas ce à quoi elle ressemble intuitivement. On dirait que nous déclarons deux variables de type int*, mais en réalité nous en déclarons une int*et une int.

Si votre langue place le type après le nom de la variable dans ses déclarations, le problème est impossible à résoudre.

Maçon Wheeler
la source
4
Je ne suis pas sûr de savoir pourquoi déplacer la déclaration de type après cela affecterait ceci. Est-ce x, y *int;deux *intou un *intet un int? Fabriquer int*ou *intun jeton au lieu de deux le résoudrait, ou utiliser un élément syntaxique supplémentaire comme le :, qui pourrait fonctionner dans les deux sens.
8bittree
@ 8bittree Toutes les langues que je connais bien et qui utilisent des déclarations en commençant par le nom utilisent un jeton supplémentaire, comme :ou as, entre le nom et le type.
Mason Wheeler
2
Et dans Go, x, y *intsignifie "déclarer deux variables, x et y, avec le type pointeur-à-int".
Vatine
10
C # utilise la syntaxe de préfixe, mais il traite int[] x, ycomme deux tableaux. C'est juste la syntaxe C idiote qui traite ces modificateurs comme des opérateurs unaires sur la variable (et un mélange de préfixe et de postfix, rien de moins) qui pose problème.
CodesInChaos