Je développe un langage que j'ai l'intention de remplacer à la fois Javascript et PHP. (Je ne vois aucun problème avec cela. Ce n'est pas comme si l'une de ces langues avait une grande base d'installation.)
L'une des choses que je voulais changer était de transformer l'opérateur d'affectation en une commande d'affectation, supprimant ainsi la possibilité d'utiliser la valeur renvoyée.
x=1; /* Assignment. */
if (x==1) {} /* Comparison. */
x==1; /* Error or warning, I've not decided yet. */
if (x=1) {} /* Error. */
Je sais que cela signifierait que ces fonctions en ligne que les gens aiment tant ne fonctionneraient plus. Je me suis dit (avec peu de preuves au-delà de mon expérience personnelle) que la grande majorité des cas, cela était vraiment destiné à être une opération de comparaison.
Ou est-ce? Existe-t-il des utilisations pratiques de la valeur de retour de l'opérateur d'affectation qui n'ont pas pu être réécrites de manière triviale? (Pour toute langue ayant un tel concept.)
while((x = getValue()) != null) {}
. Les remplacements seront plus moches car vous devrez soit utiliserbreak
soit répéter l'x = getValue
affectation.Réponses:
Techniquement, certains sucres syntaxiques peuvent être conservés même s'ils peuvent être remplacés de manière triviale, s'ils améliorent la lisibilité de certaines opérations courantes. Mais l'affectation en tant qu'expression ne relève pas de cela. Le danger de le taper à la place d'une comparaison signifie qu'il est rarement utilisé (parfois même interdit par les guides de style) et provoque une double prise chaque fois qu'il est utilisé. En d'autres termes, les avantages de lisibilité sont faibles en nombre et en ampleur.
Un regard sur les langues existantes qui le font peut être utile.
if (x)
à la placeif (x != null)
ouif (x != 0)
selon le type dex
.Cependant, Python permet un cas particulier, l' attribution de plusieurs noms à la fois:
a = b = c
. Ceci est considéré comme une déclaration équivalente àb = c; a = b
, et il est parfois utilisé, il peut donc être utile d'ajouter à votre langue également (mais je ne le suerais pas, car cet ajout devrait être rétrocompatible).la source
a = b = c
ce que les autres réponses n'évoquent pas vraiment.:=
pour l'affectation.=
est l'affectation,==
est la comparaison.if (a = true)
va lancer un avertissement C4706 (The test value in a conditional expression was the result of an assignment.
). GCC avec C lancera également awarning: suggest parentheses around assignment used as truth value [-Wparentheses]
. Ces avertissements peuvent être réduits au silence avec un jeu de parenthèses supplémentaire, mais ils sont là pour encourager à indiquer explicitement que l'affectation était intentionnelle.a = true
est évalué en booléen et n'est donc pas une erreur, mais il déclenche également un avertissement associé en C #.De manière générale, non. L'idée d'avoir la valeur d'une expression d'affectation comme étant la valeur qui a été attribuée signifie que nous avons une expression qui peut être utilisée à la fois pour son effet secondaire et sa valeur , et qui est considérée par beaucoup comme déroutante.
Les utilisations courantes consistent généralement à rendre les expressions compactes:
a la sémantique en C # de "convertir z au type de y, affecter la valeur convertie à y, la valeur convertie est la valeur de l'expression, convertir cela au type de x, affecter à x".
Mais nous sommes déjà dans le domaine des effets secondaires impertatifs dans un contexte de déclaration, donc il y a vraiment très peu d'avantages convaincants
De même avec
être un raccourci pour
Encore une fois, dans le code d'origine, nous utilisons une expression à la fois pour ses effets secondaires et sa valeur, et nous faisons une déclaration qui a deux effets secondaires au lieu d'un. Les deux sont malodorants; essayez d'avoir un effet secondaire par instruction, et utilisez des expressions pour leurs valeurs, pas pour leurs effets secondaires.
Si vous voulez vraiment être audacieux et souligner que l'affectation est une déclaration et non une égalité, alors mon conseil est: faites-en clairement une déclaration d'affectation .
Le rouge. Ou
ou encore mieux:
Ou encore mieux
Il n'y a absolument aucun moyen de les confondre
x == 1
.la source
x[⍋x←6?40]
APL avait besoin de son propre clavier spécial, mais c'était une langue assez réussie.a+b*c --> x
? Cela me semble étrange.De nombreux langages choisissent la voie pour faire de l'affectation une instruction plutôt qu'une expression, y compris Python:
et Golang:
D'autres langues n'ont pas d'affectation, mais plutôt des liaisons de portée, par exemple OCaml:
Cependant,
let
c'est une expression elle-même.L'avantage de permettre l'affectation est que nous pouvons vérifier directement la valeur de retour d'une fonction à l'intérieur du conditionnel, par exemple dans cet extrait de code Perl:
Perl étend en outre la déclaration à cette conditionnelle uniquement, ce qui la rend très utile. Il avertira également si vous affectez à l'intérieur d'un conditionnel sans y déclarer une nouvelle variable -
if ($foo = $bar)
avertira,if (my $foo = $bar)
ne le fera pas.Faire l'affectation dans une autre déclaration est généralement suffisant, mais peut poser des problèmes de portée:
Golang s'appuie fortement sur les valeurs de retour pour la vérification des erreurs. Il permet donc à un conditionnel de prendre une instruction d'initialisation:
D'autres langues utilisent un système de types pour interdire les expressions non booléennes à l'intérieur d'un conditionnel:
Bien sûr, cela échoue lors de l'utilisation d'une fonction qui renvoie un booléen.
Nous avons maintenant vu différents mécanismes pour se défendre contre une affectation accidentelle:
let
liaisonsJe les ai classés par ordre croissant de préférence - les affectations à l'intérieur des expressions peuvent être utiles (et il est simple de contourner les problèmes de Python en ayant une syntaxe de déclaration explicite et une syntaxe d'argument nommée différente). Mais il est permis de les interdire, car il existe de nombreuses autres options dans le même sens.
Le code sans bogue est plus important que le code laconique.
la source
Vous avez dit: "Je me suis dit (avec peu de preuves au-delà de mon expérience personnelle) que la grande majorité des cas, cela était vraiment destiné à être une opération de comparaison."
Pourquoi ne pas résoudre le problème?
Au lieu de = pour l'affectation et == pour le test d'égalité, pourquoi ne pas utiliser: = pour l'affectation et = (ou même ==) pour l'égalité?
Observer:
Si vous voulez qu'il soit plus difficile pour le programmeur de confondre affectation et égalité, rendez-le plus difficile.
En même temps, si vous vouliez VRAIMENT résoudre le problème, vous supprimeriez le crock C qui prétendait que les booléens n'étaient que des entiers avec des noms de sucre symboliques prédéfinis. Faites-en un type complètement différent. Ensuite, au lieu de dire
vous forcez le programmeur à écrire:
Le fait est que l'affectation en tant qu'opérateur est une construction très utile. Nous n'avons pas éliminé les lames de rasoir parce que certaines personnes se coupaient. Au lieu de cela, le roi Gillette a inventé le rasoir de sécurité.
la source
:=
pour l'affectation et=
pour l'égalité pourrait résoudre ce problème, mais au prix d'aliéner tous les programmeurs qui n'ont pas grandi en utilisant un petit ensemble de langages non traditionnels. (2) Les types autres que les bools étant autorisés dans des conditions ne sont pas toujours dus au mélange des bools et des entiers, il suffit de donner une interprétation vrai / faux aux autres types. Un langage plus récent qui n'a pas peur de s'écarter de C l'a fait pour de nombreux types autres que les entiers (par exemple, Python considère que les collections vides sont fausses).if (a = b)
pour lvalue a, booléen a, b). Dans une langue sans typage statique, il donne également de bien meilleurs messages d'erreur (au moment de l'analyse par rapport à l'exécution). En outre, la prévention des "erreurs a = b contre a == b" peut ne pas être le seul objectif pertinent. Par exemple, je voudrais également permettre au codeif items:
de vouloir direif len(items) != 0
, et que je devrais abandonner pour restreindre les conditions aux booléens.Pour répondre à la question, oui, il existe de nombreuses utilisations, bien qu'elles soient légèrement niches.
Par exemple en Java:
L'alternative sans utiliser l'affectation incorporée nécessite la
ob
définition hors de la portée de la boucle et deux emplacements de code distincts qui appellent x.next ().Il a déjà été mentionné que vous pouvez affecter plusieurs variables en une seule étape.
Ce genre de chose est l'utilisation la plus courante, mais les programmeurs créatifs en trouveront toujours plus.
la source
ob
objet avec chaque boucle?Puisque vous pouvez inventer toutes les règles, pourquoi autoriser maintenant l'affectation à transformer une valeur, et simplement ne pas autoriser les affectations à l'intérieur des étapes conditionnelles? Cela vous donne le sucre syntaxique pour faciliter les initialisations, tout en évitant une erreur de codage courante.
En d'autres termes, rendez cela légal:
Mais rendez cela illégal:
la source
a = b = c
semble plus orthogonal et plus facile à mettre en œuvre aussi. Ces deux approches ne sont pas d'accord sur l'affectation dans les expressions (a + (b = c)
), mais vous n'avez pas pris parti pour celles-ci, donc je suppose qu'elles n'ont pas d'importance.Par les sons de celui-ci, vous êtes sur le point de créer un langage assez strict.
Dans cet esprit, obliger les gens à écrire:
au lieu de:
peut sembler une amélioration pour empêcher les gens de faire:
quand ils voulaient faire:
mais au final, ce type d'erreurs est facile à détecter et à avertir s'il s'agit ou non d'un code légal.
Cependant, il existe des situations où:
ne signifie pas que
sera vrai.
Si c est en fait une fonction c (), elle pourrait renvoyer des résultats différents à chaque appel. (cela pourrait aussi être coûteux en calcul ...)
De même, si c est un pointeur vers du matériel mappé en mémoire, alors
sont à la fois susceptibles d'être différents et peuvent également avoir des effets électroniques sur le matériel à chaque lecture.
Il existe de nombreuses autres permutations avec le matériel où vous devez être précis sur les adresses mémoire lues, écrites et soumises à des contraintes de temps spécifiques, où effectuer plusieurs affectations sur la même ligne est rapide, simple et évident, sans les risques de temps introduction de variables temporaires
la source
a = b = c
ne l'est pasa = c; b = c
, c'estb = c; a = b
. Cela évite la duplication des effets secondaires et conserve également la modification dea
etb
dans le même ordre. De plus, tous ces arguments liés au matériel sont assez stupides: la plupart des langues ne sont pas des langues système et ne sont ni conçues pour résoudre ces problèmes ni utilisées dans des situations où ces problèmes se produisent. Cela vaut doublement pour un langage qui tente de déplacer JavaScript et / ou PHP.a=b=c
est courant / utile, ce sont des exemples d'endroits où l'ordre et le nombre d'effets secondaires doivent être pris en charge. C'est entièrement indépendant. Réécrivez l'affectation chaînée correctement et les deux variantes sont également correctes.b
dans un temp, et qui est convertie en type dea
dans un autre temp. Le moment relatif du moment où ces valeurs sont réellement stockées n'est pas spécifié. Du point de vue de la conception du langage, je pense qu'il est raisonnable d'exiger que toutes les valeurs l dans une instruction à affectations multiples aient un type correspondant, et éventuellement d'exiger qu'aucune d'entre elles ne soit volatile.Le plus grand avantage pour moi d'avoir l'affectation comme expression est qu'elle permet à votre grammaire d'être plus simple si l'un de vos objectifs est que "tout est une expression" - un objectif de LISP en particulier.
Python n'a pas cela; il a des expressions et des déclarations, l'affectation étant une déclaration. Mais parce que Python définit un
lambda
formulaire comme étant un paramètre unique expression , cela signifie que vous ne pouvez pas affecter de variables à l'intérieur d'un lambda. C'est parfois gênant, mais ce n'est pas un problème critique, et c'est à peu près le seul inconvénient de mon expérience à ce que l'affectation soit une déclaration en Python.Une façon de permettre à l'affectation, ou plutôt à l'effet de l'affectation, d'être une expression sans introduire le risque d'
if(x=1)
accidents que C a est d'utiliser unelet
construction de type LISP , telle que celle(let ((x 2) (y 3)) (+ x y))
qui, dans votre langage, pourrait être considérée comme5
. L'utilisation delet
cette manière n'a pas besoin d'être techniquement affectée du tout dans votre langue, si vous définissezlet
comme créant une portée lexicale. Défini de cette façon, unelet
construction peut être compilée de la même manière que la construction et l'appel d'une fonction de fermeture imbriquée avec des arguments.D'un autre côté, si vous êtes simplement concerné par le
if(x=1)
cas, mais que vous voulez que l'affectation soit une expression comme en C, peut-être que le simple choix de jetons différents suffira. Affectation:x := 1
oux <- 1
. Comparaison:x == 1
. Erreur de syntaxe:x = 1
.la source
let
diffère de l'affectation à bien des égards que l'introduction technique d'une nouvelle variable dans une nouvelle portée. Pour commencer, cela n'a aucun effet sur le code en dehors dulet
corps de ', et nécessite donc d'imbriquer davantage tout le code (ce qui devrait utiliser la variable), un inconvénient important dans le code à affectation élevée. Si l'on devait emprunter cette voie, ceset!
serait le meilleur analogue Lisp - complètement différent de la comparaison, mais ne nécessitant pas d'imbrication ou de nouvelle portée.Effectivement. Ce n'est pas nouveau, tous les sous-ensembles sûrs du langage C ont déjà tiré cette conclusion.
MISRA-C, CERT-C et ainsi de suite toute interdiction d'affectation dans des conditions, simplement parce que c'est dangereux.
Il n'existe aucun cas où le code reposant sur l'affectation dans des conditions ne peut pas être réécrit.
En outre, ces normes mettent également en garde contre l'écriture de code qui repose sur l'ordre d'évaluation. Affectations multiples sur une seule ligne
x=y=z;
sont un tel cas. Si une ligne avec plusieurs affectations contient des effets secondaires (appeler des fonctions, accéder à des variables volatiles, etc.), vous ne pouvez pas savoir quel effet secondaire se produira en premier.Il n'y a pas de points de séquence entre l'évaluation des opérandes. Nous ne pouvons donc pas savoir si la sous-expression
y
est évaluée avant ou aprèsz
: il s'agit d'un comportement non spécifié en C. Ainsi, ce code est potentiellement non fiable, non portable et non conforme aux sous-ensembles sûrs mentionnés de C.La solution aurait été de remplacer le code par
y=z; x=y;
. Cela ajoute un point de séquence et garantit l'ordre d'évaluation.Donc, sur la base de tous les problèmes que cela a causés en C, tout langage moderne ferait bien d'interdire l'affectation à l'intérieur des conditions, ainsi que plusieurs affectations sur une seule ligne.
la source