Si je comprends bien, les conversions implicites peuvent provoquer des erreurs.
Mais cela n'a pas de sens - les conversions normales ne devraient-elles pas également provoquer des erreurs?
Pourquoi ne pas avoir
len(100)
travailler par la langue qui l'interprète (ou la compile) comme
len(str(100))
d'autant plus que c'est le seul moyen (que je connaisse) pour que ça marche. La langue sait quelle est l'erreur, pourquoi ne pas la corriger?
Pour cet exemple, j'ai utilisé Python, bien que je pense que pour quelque chose d'aussi petit, il est fondamentalement universel.
perl -e 'print length(100);'
imprime 3.str
le moyen implicite de convertir un int en un itérable? que diriez-vousrange
? oubin
(hex
,oct
) ouchr
(ouunichr
)? Tous ceux-ci renvoient des itérables, même si cela vousstr
semble le plus évident dans cette situation.Réponses:
Pour ce que ça vaut
len(str(100))
,len(chr(100))
etlen(hex(100))
sont tous différents.str
n'est pas le seul moyen de le faire fonctionner, car il existe plusieurs conversions différentes en Python d'un entier en une chaîne. L'un d'eux est bien sûr le plus courant, mais il ne va pas nécessairement de soi que c'est celui-là que vous vouliez dire. Une conversion implicite signifie littéralement "cela va sans dire".L'un des problèmes pratiques des conversions implicites est qu'il n'est pas toujours évident de savoir quelle conversion sera appliquée là où il existe plusieurs possibilités, ce qui fait que les lecteurs font des erreurs d'interprétation du code car ils ne parviennent pas à déterminer la conversion implicite correcte. Tout le monde dit toujours que ce qu'il voulait, c'est "l'interprétation évidente". C'est évident pour eux parce que c'est ce qu'ils voulaient dire. Cela pourrait ne pas être évident pour quelqu'un d'autre.
C'est pourquoi (la plupart du temps) Python préfère explicite à implicite, il préfère ne pas le risquer. Le cas principal où Python exerce une contrainte de type est en arithmétique. Elle permet
1 + 1.0
parce que l'alternative serait trop gênant pour vivre avec, mais elle ne permet pas1 + "1"
parce qu'il pense que vous devriez avoir à spécifier si vous voulez direint("1")
,float("1")
,ord("1")
,str(1) + "1"
, ou autre chose. Il ne permet pas non plus(1,2,3) + [4,5,6]
, même s'il peut définir des règles pour choisir un type de résultat, tout comme il définit des règles pour choisir le type de résultat1 + 1.0
.D'autres langues ne sont pas d'accord et ont beaucoup de conversions implicites. Plus ils incluent, moins ils deviennent évidents. Essayez de mémoriser les règles de la norme C pour les "promotions entières" et les "conversions arithmétiques habituelles" avant le petit déjeuner!
la source
len
ne peut fonctionner qu'avec un seul type, est fondamentalement erronée.str
,chr
ethex
tous renvoient le même type! J'essayais de penser à un type différent dont le constructeur peut prendre juste un entier, mais je n'ai encore rien trouvé. L'idéal serait un conteneurX
oùlen(X(100)) == 100
;-)numpy.zeros
semble un peu obscur.a == b
,b == c
maisa == c
ne pas être vrai.Vous manquez un mot: les conversions implicites peuvent provoquer des erreurs d' exécution .
Pour un cas simple comme vous le montrez, ce que vous vouliez dire est assez clair. Mais les langues ne peuvent pas fonctionner sur les cas. Ils doivent travailler avec des règles. Pour de nombreuses autres situations, il n'est pas clair si le programmeur a fait une erreur (en utilisant le mauvais type) ou si le programmeur voulait faire ce que le code suppose qu'il voulait faire.
Si le code suppose une erreur, vous obtenez une erreur d'exécution. Puisqu'ils sont fastidieux à retrouver, de nombreuses langues se trompent du côté de vous dire que vous avez foiré et de vous laisser dire à l'ordinateur ce que vous vouliez vraiment dire (corriger le bogue ou faire explicitement la conversion). D'autres langages font la supposition, car leur style se prête à un code rapide et facile à déboguer.
Une chose à noter est que les conversions implicites rendent le compilateur un peu plus complexe. Vous devez faire plus attention aux cycles (essayons cette conversion de A en B; oups qui n'a pas fonctionné, mais il y a une conversion de B en A! Et ensuite vous devez vous soucier des cycles de taille C et D aussi ), qui est une petite motivation pour éviter les conversions implicites.
la source
len(100)
à revenir3
. Je trouverais beaucoup plus intuitif qu'il calcule le nombre de bits (ou octets) dans la représentation de100
.len(100)
devrait vous donner le nombre de caractères de la représentation décimale du nombre 100. Mais c'est en fait une tonne d'hypothèses que vous faites tout en ignorant d'eux. Vous pouvez expliquer avec force pourquoi le nombre de bits, d'octets ou de caractères de la représentation hexadécimale (64
-> 2 caractères) doit être renvoyé parlen()
.Les conversions implicites sont tout à fait possibles. La situation où vous rencontrez des problèmes est lorsque vous ne savez pas de quelle manière quelque chose devrait fonctionner.
Un exemple de cela peut être vu en Javascript où l'
+
opérateur travaille de différentes manières à différents moments.Si l'un des arguments est une chaîne, alors l'
+
opérateur est une concaténation de chaîne, sinon c'est l'addition.Si on vous donne un argument et que vous ne savez pas s'il s'agit d'une chaîne ou d'un entier, et que vous souhaitez en ajouter, cela peut être un peu compliqué.
Une autre façon de gérer cela est de l'héritage de base (que perl suit - voir Programmation est difficile, allons-y Scripting ... )
Dans Basic, la
len
fonction n'a de sens que d'être invoquée sur une chaîne (docs pour Visual Basic : "Toute expression de chaîne valide ou nom de variable. Si l'expression est de type Object, la fonction Len renvoie la taille telle qu'elle sera écrite dans le fichier par la fonction FilePut. ").Perl suit ce concept de contexte. La confusion qui existe en JavaScript avec la conversion implicite des types pour l'
+
opérateur étant parfois plus et parfois concaténation ne se fait pas en Perl car+
est toujours plus et.
est toujours concaténation.Si quelque chose est utilisé dans un contexte scalaire, c'est un scalaire (par exemple, en utilisant une liste comme scalaire, la liste se comporte comme s'il s'agissait d'un nombre correspondant à sa longueur). Si vous utilisez un opérateur de chaîne (
eq
pour le test d'égalité,cmp
pour la comparaison de chaînes), le scalaire est utilisé comme s'il s'agissait d'une chaîne. De même, si quelque chose a été utilisé dans un contexte mathématique (==
pour le test d'égalité et<=>
pour la comparaison numérique), le scalaire est utilisé comme s'il s'agissait d'un nombre.La règle fondamentale pour toute programmation est "faire ce qui surprend le moins". Cela ne veut pas dire qu'il n'y a pas de surprise là-dedans, mais l'effort est de surprendre le moins la personne.
En allant chez un proche cousin de perl-php, il y a des situations où un opérateur peut agir sur quelque chose dans des contextes de chaîne ou numériques et le comportement peut surprendre les gens. L'
++
opérateur en est un exemple. Sur les chiffres, il se comporte exactement comme prévu. Lorsque vous agissez sur une chaîne, par exemple"aa"
, il incrémente la chaîne ($foo = "aa"; $foo++; echo $foo;
imprimeab
). Il roulera également de sorte queaz
lorsqu'il sera incrémentéba
. Ce n'est pas encore particulièrement surprenant.( ideone )
Cela imprime:
Bienvenue aux dangers des conversions implicites et des opérateurs agissant différemment sur la même chaîne. (Perl gère ce bloc de code un peu différemment - il décide que
"3d8"
lorsque l'++
opérateur est appliqué est une valeur numérique dès le début et passe4
immédiatement ( idéone ) - ce comportement est bien décrit dans perlop: incrémentation automatique et décrémentation automatique )Maintenant, pourquoi une langue fait quelque chose d'une manière et une autre le fait d'une autre manière, cela revient aux pensées de conception des designers. La philosophie de Perl est qu'il y a plus d'une façon de le faire - et je peux penser à un certain nombre de façons de faire certaines de ces opérations. D'un autre côté, Python a une philosophie décrite dans PEP 20 - Le Zen de Python qui stipule (entre autres): "Il devrait y avoir une - et de préférence une seule - manière évidente de le faire."
Ces différences de conception ont conduit à différentes langues. Il existe un moyen d'obtenir la longueur d'un nombre en Python. La conversion implicite va à l'encontre de cette philosophie.
Lecture connexe: Pourquoi Ruby n'a-t-il pas une conversion implicite de Fixnum en chaîne?
la source
x
ety
de manière à produire une relation d'équivalence ou un classement, mais les opérateurs de comparaison de l'IIRC Python ...ColdFusion a fait la plupart de cela. Il définit un ensemble de règles pour gérer vos conversions implicites, a un seul type de variable, et c'est parti.
Le résultat est une anarchie totale, où l'ajout de "4a" à 6 est 6,16667.
Pourquoi? Eh bien, parce que la première des deux variables est un nombre, le résultat sera donc numérique. "4a" est analysé comme une date et considéré comme "4 AM". 4 AM est 4: 00/24: 00, ou 1 / 6ème de jour (0.16667). Ajoutez 6 et vous obtenez 6.16667.
Les listes ont des caractères de séparation par défaut d'une virgule, donc si vous ajoutez un élément à une liste contenant une virgule, vous venez d'ajouter deux éléments. En outre, les listes sont des chaînes secrètes, de sorte qu'elles peuvent être analysées en tant que dates si elles ne contiennent qu'un seul élément.
La comparaison de chaînes vérifie si les deux chaînes peuvent être analysées en premier aux dates. Parce qu'il n'y a pas de type de date, il le fait. Même chose pour les nombres et les chaînes contenant des nombres, la notation octale et les booléens ("vrai" et "oui", etc.)
Plutôt que d'échouer rapidement et de signaler une erreur, ColdFusion gère à la place les conversions de types de données pour vous. Et pas dans le bon sens.
Bien sûr, vous pouvez corriger les conversions implicites en appelant des fonctions explicites comme DateCompare ... mais vous avez alors perdu les "avantages" de la conversion implicite.
Pour ColdFusion, c'est un moyen d'aider les développeurs à se renforcer. Surtout lorsque tous ces développeurs sont habitués au HTML (ColdFusion fonctionne avec des balises, il s'appelle CFML, plus tard, ils ont également ajouté des scripts via
<cfscript>
, où vous n'avez pas besoin d'ajouter des balises pour tout). Et cela fonctionne bien lorsque vous voulez simplement que quelque chose fonctionne. Mais lorsque vous avez besoin que les choses se passent de manière plus précise, vous avez besoin d'un langage qui refuse de faire des conversions implicites pour tout ce qui pourrait ressembler à une erreur.la source
Vous dites que les conversions implicites pourraient être une bonne idée pour les opérations sans ambiguïté, comme
int a = 100; len(a)
, où vous entendez évidemment convertir l'int en chaîne avant d'appeler.Mais vous oubliez que ces appels peuvent être syntaxiquement sans ambiguïté, mais ils peuvent représenter une faute de frappe faite par le programmeur qui voulait passer
a1
, qui est une chaîne. Il s'agit d'un exemple artificiel, mais avec les IDE fournissant la saisie semi-automatique pour les noms de variables, ces erreurs se produisent.Le système de type vise à nous aider à éviter les erreurs, et pour les langues qui choisissent une vérification de type plus stricte, les conversions implicites sapent cela.
Découvrez les problèmes de Javascript avec toutes les conversions implicites effectuées par ==, à tel point que beaucoup recommandent maintenant de s'en tenir à l'opérateur no-implicit-conversion ===.
la source
Considérez un instant le contexte de votre déclaration. Vous dites que c'est la "seule façon" que cela pourrait fonctionner, mais êtes-vous vraiment sûr que cela fonctionnera comme ça? Et ça:
Comme d'autres l'ont mentionné, dans votre exemple très spécifique, il semble simple pour le compilateur de comprendre ce que vous vouliez dire. Mais dans un contexte plus large de programmes plus compliqués, il n'est souvent pas du tout évident si vous vouliez entrer une chaîne, ou taper un nom de variable pour un autre, ou appeler la mauvaise fonction, ou l'une des dizaines d'autres erreurs logiques potentielles .
Dans le cas où l'interpréteur / compilateur devine ce que vous vouliez dire, parfois cela fonctionne comme par magie, et d'autres fois vous passez des heures à déboguer quelque chose qui mystérieusement ne fonctionne pas sans raison apparente. Il est beaucoup plus sûr, dans le cas moyen, d'être informé que la déclaration n'a pas de sens, et de passer quelques secondes à la corriger en fonction de ce que vous vouliez réellement dire.
la source
Les conversions implicites peuvent être très pénibles à utiliser. Dans PowerShell:
Soudain, il y a deux fois plus de tests nécessaires et deux fois plus de bugs qu'il y en aurait sans conversion implicite.
la source
Les conversions explicites sont importantes car elles expliquent clairement votre intention. Tout d'abord, l'utilisation de transtypages explicites raconte une histoire à quelqu'un qui lit votre code. Ils révèlent que vous avez intentionnellement fait ce que vous avez fait. De plus, la même chose s'applique au compilateur. Ce qui suit est illégal en C # par exemple
Le casting vous fera perdre la précision, ce qui pourrait facilement être un bug. Par conséquent, le compilateur refuse de compiler. En utilisant une distribution explicite, vous dites au compilateur: "Hé, je sais ce que je fais." Et il ira: "D'accord!" Et compilez.
la source
(X)y
À mon humble avis, la seule chose qui devrait signifier est "je pense que Y est convertible en X; faites la conversion si possible, ou lancez une exception sinon"; si une conversion réussit, il faut être capable de prédire quelle sera la valeur résultante * sans avoir à connaître de règles spéciales sur la conversion du type dey
typeX
. Si on leur demandait de convertir -1,5 et 1,5 en entiers, certains langages donneraient (-2,1); certains (-2,2), certains (-1,1) et certains (-1,2). Alors que les règles de C ne sont guère obscures ...Les langages qui prennent en charge les conversions / conversions implicites, ou le typage faible comme il est parfois appelé, feront pour vous des hypothèses qui ne correspondent pas toujours au comportement que vous vouliez ou attendiez. Les concepteurs de la langue peuvent avoir eu le même processus de réflexion que vous avez fait pour un certain type de conversion ou de séries de conversions, et dans ce cas, vous ne verrez jamais de problème; mais s'ils ne l'ont pas fait, votre programme échouera.
L'échec peut cependant être évident ou non. Étant donné que ces conversions implicites se produisent au moment de l'exécution, votre programme fonctionnera avec bonheur et vous ne verrez un problème que lorsque vous examinerez la sortie ou le résultat de votre programme. Un langage qui nécessite des transtypages explicites (certains appellent cela un typage fort) vous donnerait une erreur avant même que le programme ne commence l'exécution, ce qui est un problème beaucoup plus évident et plus facile à corriger pour que les transtypages implicites se soient mal déroulés.
L'autre jour, un de mes amis a demandé à son enfant de 2 ans ce qu'était 20 + 20 et il a répondu 2020. J'ai dit à mon ami: "Il va devenir programmeur Javascript".
En Javascript:
Ainsi, vous pouvez voir les problèmes que les conversions implicites peuvent créer et pourquoi ce n'est pas quelque chose qui peut simplement être corrigé. La solution, je dirais, consiste à utiliser des transtypages explicites.
la source