En curieux sur la page principale du site d'un langage de programmation de script, j'ai rencontré ce passage:
Lorsqu'un système devient trop gros pour rester dans votre tête, vous pouvez ajouter des types statiques.
Cela m'a fait me rappeler que dans de nombreuses guerres de religion entre les langages compilés statiques (comme Java) et les langages dynamiques et interprétés (principalement Python parce qu'il est plus utilisé, mais c'est un "problème" partagé entre la plupart des langages de script), l'une des plaintes de statique les fans de langages typés par-dessus les langages typés dynamiquement, c'est qu'ils ne s'adaptent pas bien aux grands projets parce que "un jour, vous oublierez le type de retour d'une fonction et vous devrez la rechercher, tandis qu'avec les langages typés statiquement, tout est explicitement déclaré ".
Je n'ai jamais compris des déclarations comme celle-ci. Pour être honnête, même si vous déclarez le type de retour d'une fonction, vous pouvez et vous l'oublierez après avoir écrit de nombreuses lignes de code, et vous devrez toujours revenir à la ligne dans laquelle il est déclaré à l'aide de la fonction de recherche de votre éditeur de texte pour le vérifier.
De plus, au fur et à mesure que les fonctions sont déclarées avec type funcname()...
, sans savoir que type
vous devrez rechercher sur chaque ligne dans laquelle la fonction est appelée, car vous ne savez funcname
que, en Python et autres, vous pouvez simplement rechercher def funcname
ou function funcname
ce qui ne se produit qu'une seule fois, à la déclaration.
De plus, avec les REPL, il est trivial de tester une fonction pour son type de retour avec différentes entrées, tandis qu'avec les langages typés statiquement, vous devrez ajouter quelques lignes de code et tout recompiler juste pour connaître le type déclaré.
Donc, à part connaître le type de retour d'une fonction qui n'est clairement pas un point fort des langages typés statiquement, comment le typage statique est-il vraiment utile dans des projets plus importants?
la source
Réponses:
Ce n'est pas anodin. Ce n'est pas trivial du tout . C'est très simple de le faire pour les fonctions triviales.
Par exemple, vous pourriez définir trivialement une fonction où le type de retour dépend entièrement du type d'entrée.
Dans ce cas,
getAnswer
ne pas vraiment avoir un seul type de retour. Il n'y a aucun test que vous puissiez écrire qui appelle cela avec un exemple d'entrée pour savoir quel est le type de retour. Cela dépendra toujours de l'argument réel. Lors de l'exécution.Et cela n'inclut même pas les fonctions qui, par exemple, effectuent des recherches de base de données. Ou faites des choses en fonction de l'entrée de l'utilisateur. Ou recherchez des variables globales, qui sont bien sûr de type dynamique. Ou changez leur type de retour dans des cas aléatoires. Sans oublier la nécessité de tester manuellement chaque fonction individuelle à chaque fois.
Fondamentalement, prouver le type de retour de la fonction dans le cas général est littéralement mathématiquement impossible (Halting Problem). La seule façon de garantir le type de retour est de restreindre l'entrée afin que la réponse à cette question ne relève pas du domaine du problème d'arrêt en interdisant les programmes qui ne sont pas prouvables, et c'est ce que fait la saisie statique.
Les langues typées statiquement ont des choses appelées «outils». Ce sont des programmes qui vous aident à faire des choses avec votre code source. Dans ce cas, je ferais simplement un clic droit et aller à la définition, grâce à Resharper. Ou utilisez le raccourci clavier. Ou passez la souris dessus et cela me dira quels sont les types impliqués. Je ne me soucie pas du tout de saluer les fichiers. Un éditeur de texte à lui seul est un outil pathétique pour éditer le code source d'un programme.
De mémoire,
def funcname
ne serait pas suffisant en Python, car la fonction pourrait être ré-assignée arbitrairement. Ou pourrait être déclaré à plusieurs reprises dans plusieurs modules. Ou en cours. Etc.La recherche de fichiers pour le nom de la fonction est une terrible opération primitive qui ne devrait jamais être nécessaire. Cela représente une défaillance fondamentale de votre environnement et de vos outils. Le fait que vous envisagiez même d'avoir besoin d'une recherche de texte en Python est un point énorme contre Python.
la source
Pensez à un projet avec de nombreux programmeurs, qui a changé au fil des ans. Vous devez maintenir cela. Il y a une fonction
Que diable fait-il? Quoi
v
? D'où vient l'élémentanswer
?Maintenant, nous avons plus d'informations -; il a besoin d'un type de
AnswerBot
.Si nous allons dans une langue de classe, nous pouvons dire
Maintenant, nous pouvons avoir une variable de type
AnswerBot
et appeler la méthodegetAnswer
et tout le monde sait ce qu'elle fait. Toutes les modifications sont détectées par le compilateur avant la fin des tests d'exécution. Il existe de nombreux autres exemples mais peut-être que cela vous donne l'idée?la source
Vous semblez avoir quelques idées fausses sur le travail avec de grands projets statiques qui peuvent brouiller votre jugement. Voici quelques conseils:
La plupart des personnes travaillant avec des langages typés statiquement utilisent soit un IDE pour la langue, soit un éditeur intelligent (tel que vim ou emacs) qui a une intégration avec des outils spécifiques à la langue. Il existe généralement un moyen rapide de trouver le type de fonction dans ces outils. Par exemple, avec Eclipse sur un projet Java, il existe généralement deux façons de trouver le type d'une méthode:
someVariable.
) et Eclipse recherche le type desomeVariable
et fournit une liste déroulante de toutes les méthodes définies dans ce type; lorsque je fais défiler la liste, le type et la documentation de chacun sont affichés pendant leur sélection. Notez que cela est très difficile à réaliser avec un langage dynamique, car il est difficile (ou dans certains cas impossible) pour l'éditeur de déterminer le type desomeVariable
, il ne peut donc pas générer facilement la liste correcte. Si je veux utiliser une méthode,this
je peux simplement appuyer sur ctrl + espace pour obtenir la même liste (bien que dans ce cas ce ne soit pas si difficile à réaliser pour les langages dynamiques).Comme vous pouvez le voir, c'est un peu mieux que l'outillage typique disponible pour les langages dynamiques (pas que cela soit impossible dans les langages dynamiques, car certains ont de très bonnes fonctionnalités IDE - smalltalk en est un qui me vient à l'esprit - mais c'est plus difficile pour une langue dynamique et donc moins susceptible d’être disponible).
Les outils de langage statique fournissent généralement des capacités de recherche sémantique, c'est-à-dire qu'ils peuvent trouver la définition et les références à des symboles particuliers avec précision, sans avoir besoin d'effectuer une recherche de texte. Par exemple, en utilisant Eclipse pour un projet Java, je peux mettre en surbrillance un symbole dans l'éditeur de texte et faire un clic droit dessus et choisir soit «aller à la définition» ou «trouver des références» pour effectuer l'une de ces opérations. Vous n'avez pas besoin de rechercher le texte d'une définition de fonction, car votre éditeur sait déjà exactement où il se trouve.
Cependant, l'inverse est que la recherche d'une définition de méthode par texte ne fonctionne vraiment pas aussi bien dans un grand projet dynamique que vous le suggérez, car il pourrait facilement y avoir plusieurs méthodes du même nom dans un tel projet, et vous n'avez probablement pas des outils facilement disponibles pour lever l’ambiguïté sur l’un d’eux que vous invoquez (car de tels outils sont difficiles à écrire au mieux, ou impossibles dans le cas général), vous devrez donc le faire à la main.
Il n'est pas impossible d'avoir un REPL pour une langue typée statiquement. Haskell est l'exemple qui me vient à l'esprit, mais il existe également des REPL pour d'autres langages typés statiquement. Mais le fait est que vous n'avez pas besoin d'exécuter du code pour trouver le type de retour d'une fonction dans un langage statique - il peut être déterminé par examen sans avoir à exécuter quoi que ce soit.
Les chances sont que même si vous en aviez besoin, vous n'auriez pas à tout recompiler . La plupart des langages statiques modernes ont des compilateurs incrémentiels qui ne compileront que la petite partie de votre code qui a changé, de sorte que vous pouvez obtenir un retour presque instantané pour les erreurs de type si vous en faites un. Eclipse / Java, par exemple, mettra en évidence les erreurs de frappe pendant que vous les saisissez .
la source
You seem to have a few misconceptions about working with large static projects that may be clouding your judgement.
Eh bien, je n'ai que 14 ans et je ne programme que depuis moins d'un an sur Android, donc c'est possible je suppose.Comparez avec disons, javascript, Ruby ou Smalltalk, où les développeurs redéfinissent les fonctionnalités du langage de base au moment de l'exécution. Cela rend la compréhension du grand projet plus difficile.
Les projets de plus grande envergure ont non seulement plus de gens, ils ont plus de temps. Assez de temps pour que tout le monde oublie ou passe à autre chose.
Pour l'anecdote, une de mes connaissances a une programmation sécurisée "Job For Life" à Lisp. Personne, sauf l'équipe, ne peut comprendre la base de code.
la source
Anecdotally, an acquaintance of mine has a secure "Job For Life" programming in Lisp. Nobody except the team can understand the code-base.
est-ce vraiment si mauvais? La personnalisation qu'ils ont ajoutée ne les aide-t-elle pas à être plus productifs?Il ne s'agit pas d'oublier le type de retour - cela va toujours arriver. Il s'agit de l'outil capable de vous faire savoir que vous avez oublié le type de retour.
C'est une question de syntaxe, qui n'a aucun lien avec le typage statique.
La syntaxe de la famille C est en effet peu conviviale lorsque vous souhaitez rechercher une déclaration sans disposer d'outils spécialisés. Les autres langues n'ont pas ce problème. Voir la syntaxe de déclaration de Rust:
Toute langue peut être interprétée et toute langue peut avoir un REPL.
Je répondrai de manière abstraite.
Un programme se compose de diverses opérations et ces opérations sont présentées telles qu'elles sont en raison de certaines hypothèses émises par le développeur.
Certaines hypothèses sont implicites et d'autres sont explicites. Certaines hypothèses concernent une opération à proximité, certaines concernent une opération à distance. Une hypothèse est plus facile à identifier lorsqu'elle est exprimée explicitement et le plus près possible des endroits où sa valeur de vérité est importante.
Un bogue est la manifestation d'une hypothèse qui existe dans le programme mais qui ne tient pas dans certains cas. Pour retrouver un bogue, nous devons identifier l'hypothèse erronée. Pour supprimer le bogue, nous devons soit supprimer cette hypothèse du programme, soit modifier quelque chose pour que l'hypothèse se vérifie.
Je voudrais classer les hypothèses en deux types.
Le premier type sont les hypothèses qui peuvent ou non tenir, selon les entrées du programme. Pour identifier une hypothèse erronée de ce type, nous devons rechercher dans l'espace toutes les entrées possibles du programme. En utilisant des suppositions éclairées et une pensée rationnelle, nous pouvons affiner le problème et chercher dans un espace beaucoup plus petit. Mais quand un programme se développe encore un peu, son espace d'entrée initial augmente à un rythme énorme - au point où il peut être considéré comme infini à toutes fins pratiques.
Le deuxième type sont les hypothèses qui valent définitivement pour toutes les entrées, ou qui sont définitivement erronées pour toutes les entrées. Lorsque nous identifions une hypothèse de ce type comme erronée, nous n'avons même pas besoin d'exécuter le programme ou de tester une entrée. Lorsque nous identifions une hypothèse de ce type comme correcte, nous avons un suspect de moins à prendre en compte lorsque nous recherchons un bogue ( n'importe quel bogue). Par conséquent, il est utile que le plus grand nombre possible d'hypothèses appartiennent à ce type.
Pour placer une hypothèse dans la deuxième catégorie (toujours vrai ou toujours faux, indépendamment des entrées), nous avons besoin d'un minimum d'informations pour être disponible à l'endroit où l'hypothèse est faite. Dans le code source d'un programme, les informations deviennent périmées assez rapidement (par exemple, de nombreux compilateurs ne font pas d'analyse interprocédurale, ce qui fait de tout appel une limite stricte pour la plupart des informations). Nous avons besoin d'un moyen de garder à jour les informations requises (valides et à proximité).
Une façon consiste à avoir la source de ces informations aussi près que possible de l'endroit où elles vont être consommées, mais cela peut ne pas être pratique pour la plupart des cas d'utilisation. Une autre façon consiste à répéter fréquemment les informations, en renouvelant leur pertinence dans le code source.
Comme vous pouvez déjà le deviner, les types statiques sont exactement cela - des balises d'informations de type dispersées à travers le code source. Ces informations peuvent être utilisées pour placer la plupart des hypothèses sur l'exactitude des types dans la deuxième catégorie, ce qui signifie que presque toutes les opérations peuvent être classées comme toujours correctes ou toujours incorrectes en ce qui concerne la compatibilité des types.
Lorsque nos types sont incorrects, l'analyse nous fait gagner du temps en signalant le bogue plus tôt que tard. Lorsque nos types sont corrects, l'analyse nous fait gagner du temps en s'assurant que lorsqu'un bogue se produit, nous pouvons immédiatement exclure les erreurs de type.
la source
Vous vous souvenez du vieil adage "ordures dans, ordures hors", eh bien, c'est ce que la saisie statique aide à empêcher. Ce n'est pas une panacée universelle mais la rigueur sur le type de données qu'une routine accepte et renvoie signifie que vous avez une certaine assurance que vous travaillez correctement avec elle.
Une routine getAnswer qui retourne un entier ne sera donc pas utile lorsque vous essayez de l'utiliser dans un appel basé sur une chaîne. Le typage statique vous dit déjà de faire attention, que vous faites probablement une erreur. (et bien sûr, vous pouvez ensuite le remplacer, mais vous devez savoir exactement ce que vous faites et le spécifier dans le code à l'aide d'un transtypage. En général, cependant, vous ne voulez pas faire cela - pirater un une cheville ronde dans un trou carré ne fonctionne jamais bien à la fin)
Vous pouvez maintenant aller plus loin en utilisant des types complexes, en créant une classe qui a des fonctionnalités de minerai, vous pouvez commencer à les faire circuler et vous obtenez soudainement beaucoup plus de structure dans votre programme. Les programmes structurés sont ceux qui sont beaucoup plus faciles à faire fonctionner correctement et à maintenir.
la source