Il semble que tous les nouveaux langages de programmation ou du moins ceux qui sont devenus populaires utilisent l'inférence de type. Même Javascript a obtenu des types et des inférences de types via diverses implémentations (Acscript, TypeScript, etc.). Cela me semble bien, mais je me demande s'il y a des compromis ou pourquoi disons Java ou les anciens bons langages n'ont pas d'inférence de type
Le système de types de Haskell est entièrement déductible (en laissant de côté la récursion polymorphe, certains langageextensions , et le redouté restriction du monomorphisme ), mais les programmeurs fournissent encore fréquemment du type annotations dans le code source même quand elles n'en ont pas besoin. Pourquoi?
map :: (a -> b) -> [a] -> [b]
. Sa forme plus générale (fmap :: Functor f => (a -> b) -> f a -> f b
) s'applique à tous les Functor
, pas seulement aux listes. Mais il a été estimé que map
serait plus facile à comprendre pour les débutants, il vit donc aux côtés de son grand frère.Dans l'ensemble, les inconvénients d'un système statiquement typé mais déductible sont à peu près les mêmes que les inconvénients de la frappe statique en général, une discussion bien rodée sur ce site et d'autres de pages de guerres de flammes). Bien sûr, certains desdits inconvénients sont améliorés par la plus petite quantité d'annotations de type dans un système inférable. De plus, l'inférence de type a ses propres avantages: développement par trous ne serait pas possible sans l'inférence de type.
Java * prouve qu'un langage nécessitant trop d'annotations de type devient ennuyeux, mais avec trop peu, vous perdez les avantages que j'ai décrits ci-dessus. Les langues avec inférence de type opt-out établissent un équilibre agréable entre les deux extrêmes.
* Même Java, ce grand bouc émissaire, effectue une certaine quantité d'inférence de type local. Dans une instruction comme Map<String, Integer> = new HashMap<>();
, vous n'avez pas besoin de spécifier le type générique du constructeur. D'un autre côté, les langages de style ML sont généralement globalement inférables.
En C #, l'inférence de type se produit au moment de la compilation, donc le coût d'exécution est nul.
Pour des raisons de style, var
est utilisé dans les situations où il est gênant ou inutile de spécifier manuellement le type. Linq est une de ces situations. Un autre est:
var s = new SomeReallyLongTypeNameWith<Several, Type, Parameters>(andFormal, parameters);
sans quoi vous répéteriez votre nom de type très long (et les paramètres de type) plutôt que de simplement dire var
.
Utiliser le nom réel du type lorsqu'il est explicite améliore la clarté du code.
Il y a des situations où l'inférence de type ne peut pas être utilisée, comme les déclarations de variable membre dont les valeurs sont définies au moment de la construction, ou où vous voulez vraiment que l'intellisense fonctionne correctement (Hackerrank's IDE n'intellisense pas le membres de la variable, sauf si vous déclarez le type explicitement).
Bonne question!
Et il y a des langages plus ésotériques qui ne peuvent pas faire leur bizarrerie sans annotation de type explicite. Jusqu'à présent, il n'y en a aucun que je sache qui soit assez commun/populaire/prometteur pour le mentionner, sauf en passant.
Cela me semble bien, mais je me demande s'il y a des compromis ou pourquoi disons Java ou les anciens bons langages n'ont pas d'inférence de type
Java se trouve être l'exception plutôt que la règle ici. Même C++ (que je crois qualifier de "bon vieux langage" :)) prend en charge l'inférence de type avec le mot clé auto
depuis la norme C++ 11. Il fonctionne non seulement avec la déclaration de variable, mais aussi comme type de retour de fonction, ce qui est particulièrement pratique avec certaines fonctions de modèle complexes.
Le typage implicite et l'inférence de type ont de nombreux bons cas d'utilisation, et il y a aussi certains cas d'utilisation où vous ne devriez vraiment pas le faire. C'est parfois une question de goût et un sujet de débat.
Mais qu'il existe sans aucun doute de bons cas d'utilisation, est en soi une raison légitime pour tout langage de le mettre en œuvre. Ce n'est pas non plus une fonctionnalité difficile à implémenter, aucune pénalité d'exécution et n'affecte pas le temps de compilation de manière significative.
Je ne vois pas de réel inconvénient à donner au développeur la possibilité d'utiliser l'inférence de type.
Certains répondeurs se demandent à quel point la frappe explicite est bonne parfois, et ils ont certainement raison. Mais ne pas prendre en charge le typage implicite signifierait qu'un langage applique un typage explicite tout le temps.
Donc, le véritable inconvénient est quand un langage ne le fait pas supporte le typage implicite, car avec cela, il déclare qu'il n'y a aucune raison légitime pour le développeur de l'utiliser, ce qui n'est pas vrai.
La principale distinction entre un système d'inférence de type Hindley-Milner et l'inférence de type Go est la direction du flux d'informations. Dans HM, saisissez les flux d'informations vers l'avant et vers l'arrière via l'unification; dans Go, saisissez uniquement les flux d'informations vers l'avant: il calcule uniquement les substitutions vers l'avant.
L'inférence de type HM est une splendide innovation qui fonctionne bien avec les langages fonctionnels de type polymorphe, mais les auteurs de Go diraient probablement qu'elle essaie d'en faire trop:
Le fait que l'information circule à la fois vers l'avant et vers l'arrière signifie que l'inférence de type HM est une affaire très non locale; en l'absence d'annotations de type, chaque ligne de code de votre programme peut contribuer à la saisie d'une seule ligne de code. Si vous ne disposez que d'une substitution directe, vous savez que la source d'une erreur de type doit être dans le code qui précède votre erreur; pas le code qui vient après.
Avec l'inférence de type HM, vous avez tendance à penser à contraintes: lorsque vous utilisez un type, vous contraignez les types possibles qu'il peut avoir. À la fin de la journée, il peut y avoir certaines variables de type qui sont laissées totalement sans contrainte. La perspicacité de l'inférence de type HM est que, ces types n'ont vraiment pas d'importance, et donc ils sont transformés en variables polymorphes. Cependant, ce polymorphisme supplémentaire peut être indésirable pour un certain nombre de raisons. Tout d'abord, comme certaines personnes l'ont souligné, ce polymorphisme supplémentaire pourrait être indésirable: HM concluant un faux, de type polymorphe pour certains faux codes, et conduisant à d'étranges erreurs plus tard. Deuxièmement, lorsqu'un type est laissé polymorphe, cela peut avoir des conséquences sur le comportement d'exécution. Par exemple, un résultat intermédiaire trop polymorphe est la raison pour laquelle "montrer. lire 'est considéré comme ambigu à Haskell; comme autre exemple, les valeurs polymorphes doivent être évaluées plusieurs fois pour chaque type auquel elles sont évaluées, motivant la restriction du monomorphisme.