web-dev-qa-db-fra.com

Pourquoi le type recherche-t-il le nom de la variable dans les langages de programmation modernes?

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

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

58
Andre Polykanine

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 suffisamment intelligentes pour se remplir elles-mêmes lorsque vous fournissez une expression d'initialisation qui a un type facilement déterminé.

Cela est important car le fait de placer les parties facultatives d'une expression plus à droite réduit l'ambiguïté d'analyse et augmente la cohérence entre les expressions qui utilisent cette partie et celles qui n'en utilisent pas. Il est juste plus simple d'analyser une déclaration lorsque vous savez que le mot-clé var et le nom de la variable sont tous deux obligatoires avant de passer aux éléments facultatifs. En théorie, toutes ces choses qui facilitent l'analyse pour les ordinateurs devraient améliorent également la lisibilité globale pour les humains, mais c'est beaucoup plus discutable.

Cet argument devient particulièrement fort lorsque vous considérez tous les modificateurs de type facultatifs dont dispose un langage "non moderne" comme C++, tels que * pour les pointeurs, & pour les références, const, volatile et ainsi de suite. Une fois que vous avez ajouté des virgules pour plusieurs déclarations, vous commencez à obtenir des ambiguïtés vraiment étranges comme int* a, b; ne fait pas de b un pointeur.

Même C++ prend désormais en charge les déclarations "tapez à droite" sous la forme de auto x = int{ 4 }; et cela présente certains avantages .

65
Ixrec

Pourquoi dans presque tous les langages de programmation modernes (Go, Rust, Kotlin, Swift, Scala, Nim, même Python dernière version) 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 des langages de programmation nouveaux qui ont le type avant l'identifiant. C♯, D ou Ceylan, par exemple.
  • Le fait d'avoir le type après l'identifiant n'est pas un phénomène nouveau, cela 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 commence ~ 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 1986-1988) et Haskell (1989).

Pascal, ML, CLU, Modula-2 et Miranda étaient tous des langages très influents, il n'est donc pas surprenant que ce style de déclarations de type soit resté populaire.

Pourquoi x: int = 42 et pas int x = 42? Cette dernière n'est-elle pas plus lisible que la première?

La lisibilité est une question de familiarité. Personnellement, je trouve que le chinois est illisible, mais apparemment, les Chinois ne le font pas. Ayant appris Pascal à l'école, barboté avec Eiffel, F♯, Haskell et Scala, et ayant étudié TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml,…, cela me semble 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, elle est assez persistante: comme je l'ai mentionné, cela remonte à cent ans en mathématiques.

L'un des principaux avantages d'avoir le type après l'identifiant, c'est qu'il est facile de laisser le type si vous voulez qu'il soit déduit. Si vos déclarations ressemblent à ceci:

val i: Int = 10

Ensuite, il est trivial de laisser de côté le type et de le déduire comme ceci:

val i = 10

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

Int i = 10

Ensuite, il devient difficile pour l'analyseur de distinguer une expression d'une déclaration:

i = 10

La solution que les concepteurs de langage proposent généralement consiste à introduire un mot clé "Je ne veux pas écrire un 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 essentiellement écrire explicitement un type qui dit que vous n'écrivez pas de type. Hein? Il serait beaucoup plus facile et plus judicieux de le laisser de côté, mais cela rend la grammaire beaucoup plus complexe.

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

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

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

    Pourquoi les déclarations sont-elles inversées?

    Ils ne sont inversés que si vous avez l'habitude de C. En C, la notion est qu'une variable est déclarée comme une expression dénotant 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érer les pointeurs de fonction. Aller sépare principalement la syntaxe d'expression et de type et cela simplifie les choses (en utilisant le préfixe * pour les pointeurs est une exception qui confirme la règle). En C, la déclaration

    int* a, b;
    

    déclare que a est un pointeur mais pas b; à Go

    var a, b *int
    

    déclare les deux être des pointeurs. C'est plus clair et plus régulier. Également := un bref formulaire de déclaration soutient qu'une déclaration de variable complète devrait présenter le même ordre que := donc

    var a uint64 = 1
    

    a le même effet que

    a := uint64(1)
    

    L'analyse est également simplifiée en ayant une grammaire distincte pour les types qui n'est pas seulement la grammaire d'expression; des mots clés tels que func et chan gardent 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 active certaines fonctionnalités syntaxiques de Nice. Par exemple, il est facile de laisser de côté les annotations de type. Scala a également prouvé assez bien 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 . En utilisant le "variable: Type "la syntaxe est facile - il suffit de laisser de côté les deux-points et le type. Mais en style C" Type variable "syntaxe, vous ne pouvez pas simplement laisser de côté le type - il n'y aurait plus de marqueur pour démarrer la définition. Vous auriez besoin d'un autre mot-clé pour être un espace réservé pour un type manquant (C♯ 3.0, qui fait une inférence de type, utilise var à cet effet). 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 utilisent la syntaxe de type préfixe :

Préfixe au lieu d'annotations de type postfix

Pourquoi suivez-vous C et Java en plaçant les annotations de type en premier, au lieu de Pascal et ML en les plaçant 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 ceci:

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

Et nous ne comprenons tout simplement pas comment quelqu'un pourrait penser le contraire!

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

32
Jörg W Mittag

Soit dit en passant, Pascal n'est pas un nouveau langage. C'était une langue académique, conçue à partir de zéro.

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

C # et Java provient de C donc ils devaient respecter "l'art antérieur", donc ne pas confondre le programmeur expérimenté.

5
Martin Maat

Pourquoi x: int = 42 et pas int x = 42? Cette dernière n'est-elle pas plus lisible que la première?

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

Il s'agit d'une déclaration réelle et valide en C, mais elle ne fait pas ce à quoi elle ressemble intuitivement. Il semble que nous déclarions deux variables de type int*, mais en fait nous en déclarons un int* et un int.

Si votre langue met le type après le ou les noms de variables dans ses déclarations, ce problème est impossible à rencontrer.

1
Mason Wheeler