J'ai lu sur la commodité (ONU) d'avoir null
au lieu de (par exemple) Maybe
. Après avoir lu - cet article, Je suis convaincu qu'il serait beaucoup mieux d'utiliser Maybe
(ou quelque chose de similaire). Cependant, je suis surpris de voir que tous les langages de programmation "bien connus" impératif ou orientés objet utilisent toujours null
(qui permet un accès non coché aux types qui peuvent représenter une valeur "rien") et que Maybe
est principalement utilisé dans les langages de programmation fonctionnelle.
À titre d'exemple, examinez le code C # suivant:
void doSomething(string username)
{
// Check that username is not null
// Do something
}
Quelque chose sent mauvais ici ... Pourquoi devrions-nous vérifier si l'argument est nul? Ne devons-nous pas supposer que chaque variable contient une référence à un objet? Comme vous pouvez le constater, le problème est que, par définition, presque toutes les variables peuvent contenir une référence nulle. Et si nous pouvions décider Quelles variables sont "nullables" et qui non? Cela nous sauverait beaucoup d'efforts tout en débogage et à la recherche d'une "NullReferenceExceptionException". Imaginez que, par défaut, aucun type ne peut contenir une référence null . Au lieu de cela, vous seriez état explicitement qu'une variable peut contenir une référence null , uniquement si vous en avez vraiment besoin. C'est l'idée derrière peut-être. Si vous avez une fonction que dans certains cas échoue (par exemple la division par zéro), vous pouvez retourner un Maybe<int>
, indiquant explicitement que le résultat peut être un int, mais aussi rien! Ceci est une raison pour préférer peut-être au lieu de null. Si vous êtes intéressé par plus d'exemples, alors je suggère de lire cet article.
Les faits sont que, malgré les inconvénients de la plupart des types nullables par défaut, la plupart des langages de programmation OO le font-ils. C'est pourquoi je m'interroge sur:
null
dans votre langage de programmation au lieu de Maybe
? Y a-t-il des raisons de tous ou est-ce juste "bagages historiques"?Assurez-vous de bien comprendre la différence entre NULL et peut-être avant de répondre à cette question.
Je crois que c'est principalement des bagages historiques.
La langue la plus importante et la plus ancienne avec null
est C et C++. Mais ici, null
a du sens. Les pointeurs sont toujours un concept assez numérique et de bas niveau. Et comment quelqu'un d'autre a dit, dans la mentalité des programmeurs C et C++, devoir dire explicitement que le pointeur peut être null
n'a pas de sens.
Deuxième ligne vient Java. CONSIDÉRANT Java Developers essayait de se rapprocher de C++, afin de pouvoir effectuer une transition de C++ à Java plus simple, ils ne voulaient probablement pas gâcher avec telle Concept de base de la langue. En outre, la mise en œuvre explicite null
nécessiterait beaucoup plus d'efforts, car vous devez vérifier si la référence non nulle est réellement définie après l'initialisation.
Toutes les autres langues sont identiques telles que Java. Ils copient généralement la voie C++ ou Java le fait-il et envisagent de la manière dont le concept de base de types implicites null
de types de référence est, il devient vraiment difficile de concevoir une langue qui utilise explicitement null
.
En fait, null
est une excellente idée. Compte tenu d'un pointeur, nous voulons désigner que ce pointeur ne fait pas référence à une valeur valide. Nous prenons donc un emplacement de mémoire, déclarez-le invalide et tenez-vous à cette convention (une convention parfois appliquée avec Segfault). Maintenant, chaque fois que j'ai un pointeur, je peux vérifier s'il contient Nothing
(ptr == null
) ou Some(value)
(ptr != null
, value = *ptr
). Je veux que vous compreniez que cela équivaut à un type Maybe
.
Les problèmes de ce sont:
Dans de nombreuses langues, le système de type n'assiste pas ici pour garantir une référence non nulle.
Ce sont des bagages historiques, autant d'impératifs traditionnels ou OOP Langues n'ont eu que des progrès incrémentiels dans leurs systèmes de type par rapport aux prédécesseurs. De petits changements ont l'avantage que de nouvelles langues sont plus faciles à apprendre. C # est une langue traditionnelle qui a introduit des outils de niveau linguistique pour mieux gérer les nuls.
Les designers de l'API peuvent revenir null
sur une défaillance, mais pas une référence à la chose réelle elle-même sur le succès. Souvent, la chose (sans référence) est renvoyée directement. Cet aplatissement d'un niveau de pointeur rend impossible d'utiliser null
comme valeur.
C'est juste la paresse du côté designer et ne peut être aidée sans appliquer la nicheur approprié avec un système de type approprié. Certaines personnes pourraient également essayer de justifier cela avec des considérations de performance ou avec l'existence de chèques optionnels (une collection peut renvoyer null
ou l'élément lui-même, mais également fournir une méthode contains
).
Dans HASKELL, il y a une vue nette sur le type Maybe
comme une monade. Cela facilite la composition des transformations sur la valeur contenue.
D'autre part, des langues de bas niveau, telles que c à peine traiter des tableaux de type distinct, alors je ne suis pas sûr de ce que nous attendons. In OOP Languies avec polymorphisme paramétré, un type d'exécution-vérifié Maybe
est plutôt trivial à mettre en œuvre.
Ma compréhension est que null
était une construction nécessaire pour obtenir des langages de programmation abstraite hors de l'assemblage.1 Les programmeurs avaient besoin de la possibilité d'indiquer qu'un pointeur ou une valeur d'enregistrement était not a valid value
Et null
est devenu le terme commun pour cette signification.
Renforcer le point que null
n'est qu'une convention pour représenter un concept, la valeur réelle pour null
utilisé pour pouvoir/peut varier en fonction du langage et de la plate-forme de programmation.
Si vous conceviez une nouvelle langue et que vous vouliez éviter null
, mais utilisez plutôt maybe
, alors j'encouragerais un terme plus descriptif tel que not a valid value
Ou navv
indiquer l'intention. Mais le nom de cette non-valeur est un concept distinct de savoir si vous devriez être Autoriser Les non-valeurs pour même exister dans votre langue.
Avant de pouvoir décider de l'un ou l'autre de ces deux points, vous devez définir la signification de maybe
signifierait pour votre système. Vous trouverez peut-être que c'est juste une renommée de la signification de null
de not a valid value
Ou vous trouverez peut-être qu'il a une sémantique différente pour votre langue.
De même, la décision de vérifier l'accès à l'accès null
ou de référence est une autre décision de conception de votre langue.
Pour fournir un peu d'histoire, C
avait une hypothèse implicite que les programmeurs comprenaient ce qu'ils tentaient de faire lors de la manipulation de la mémoire. Comme il s'agissait d'une abstraction supérieure à l'assemblée et des langues impératives qui l'ont précédée, je vous aventuerais que la pensée de protéger le programmeur du programmeur d'une référence incorrecte n'a pas rencontré leur esprit.
Je crois que certains compilateurs ou leur outillage supplémentaire peuvent fournir une mesure de la vérification contre l'accès non valide du pointeur. Donc, d'autres ont noté cette question potentielle et prises des mesures pour se protéger.
Que vous devriez ou non si vous devriez le laisser dépendre de ce que vous voulez que votre langue accomplisse et quel degré de responsabilité vous souhaitez appuyer sur les utilisateurs de votre langue. Cela dépend également de votre capacité à créer un compilateur pour restreindre ce type de comportement.
Donc, pour répondre à vos questions:
"Quels types d'arguments ..." - Eh bien, cela dépend de ce que vous voulez que la langue fasse. Si vous souhaitez simuler un accès en métal nu, vous voudrez peut-être le permettre.
"Est-ce juste des bagages historiques?" Peut-être que peut-être pas. null
a certainement eu/a signification pour un certain nombre de langues et aide à conduire l'expression de ces langues. Le précédent historique peut avoir affecté des langues plus récentes et leur autorisation de null
, mais c'est un peu beaucoup à faire signe à vos mains et à déclarer le concept des bagages historiques inutiles.
1Voir cet article Wikipedia Bien que le crédit soit donné pour huiler pour les valeurs nulles et les langues orientées objet. Je crois que les langues impératives ont progressé le long d'un arbre généalogique différent de celui de l'algol.
Si vous regardez les exemples de l'article que vous avez cité, la plupart du temps en utilisant Maybe
ne raccourcit pas le code. Cela n'écarte pas la nécessité de vérifier Nothing
. La seule différence est qu'il vous rappelle de le faire via le système de type.
Notez, je dis "Rappelez", ne pas forcer. Les programmeurs sont paresseux. Si un programmeur est convaincu une valeur ne peut éventuellement être Nothing
, ils vont à la déréférence le Maybe
sans le vérifier, tout comme leur dénonosité un pointeur nulle maintenant. Le résultat final est que vous convertissez une exception de pointeur NULL en une exception "vide désinférencée peut-être".
Le même principe de la nature humaine s'applique dans d'autres domaines où les langages de programmation tentent de forcer les programmeurs à faire quelque chose. Par exemple, le Java concepteurs a essayé de forcer les gens à gérer la plupart des exceptions près, ce qui a entraîné une grande partie de la chaudron qui s'ignore silencieusement ou propage aveuglément des exceptions.
Ce qui fait Maybe
est agréable, c'est que beaucoup de décisions sont effectuées par correspondance et polymorphisme des motifs au lieu de contrôles explicites. Par exemple, vous pouvez créer des fonctions distinctes processData(Some<T>)
et processData(Nothing<T>)
, que vous ne pouvez pas faire avec null
. Vous déplacez automatiquement votre manipulation d'erreur sur une fonction distincte, qui est très souhaitable dans la programmation fonctionnelle où les fonctions sont transmises et évaluées paresseusement plutôt que toujours appelées de manière descendante. Dans OOP, le moyen préféré de découpler votre code de traitement des erreurs est avec des exceptions.
Le concept de null
peut facilement être retrouvé sur c mais ce n'est pas l'endroit où le problème réside.
Mon langage de choix quotidien est C # et je garderais null
avec une différence. C # a deux types de types, et Références . Les valeurs ne peuvent jamais être null
, mais il y a des moments où j'aimerais pouvoir exprimer qu'aucune valeur n'est parfaite. Pour ce faire, c # utilise Nullable
Types, donc int
serait la valeur et int?
la valeur nullable. Voici comment je pense que les types de référence devraient également fonctionner.
Voir également: NULL référence peut ne pas être une erreur :
Les références nulles sont utiles et parfois indispensables (considérez la quantité de problème si vous risquez ou non de renvoyer une chaîne en C++). L'erreur n'est vraiment pas dans l'existence des pointeurs NULL, mais dans la façon dont le système de type les gère. Malheureusement, la plupart des langues (C++, Java, C #) ne les traitent pas correctement.
Maybe
est un moyen très fonctionnel de penser à un problème - il y a une chose, et elle peut ou non avoir une valeur définie. Dans un sens orienté objet, cependant, nous remplaçons cette idée d'une chose (peu importe si elle a une valeur ou non) avec un objet. Clairement, un objet a une valeur. Si ce n'est pas le cas, nous disons que l'objet est null
, mais ce que nous voulons vraiment dire, c'est qu'il n'y a pas de objet du tout. La référence que nous avons devant les points d'objet à rien. Traduire Maybe
dans un OO Concept ne fait rien de nouveau - en fait, il fait simplement un code plus grandement encombré. Vous devez toujours avoir une référence nulle pour la valeur de la valeur du Maybe<T>
. Vous devez toujours faire des chèques nuls (en fait, vous devez faire beaucoup plus de chèques nuls, encombrer votre code), même si elles sont maintenant appelées "peut-être chèques". Bien sûr, vous écrirez un code plus robuste comme l'auteur affirme, mais je dirais que ce n'est que le cas car vous avez rendu la langue beaucoup plus abstraite et obtus, nécessitant un niveau de travail inutile dans la plupart des cas. Je prendrais volontairement une nullreferenceException une fois dans un certain temps que de traiter le code de Spaghetti faire un chèque peut-être à chaque fois que j'accède à une nouvelle variable.
"Maybe
", comme écrit, est une construction de niveau supérieur à celle de NULL. En utilisant plus de mots pour le définir, c'est peut-être "un pointeur à une chose ou un pointeur à rien, mais le compilateur n'a pas encore reçu suffisamment d'informations pour déterminer lequel". Cela vous oblige à vérifier explicitement chaque valeur constamment, sauf si vous créez une spécification de compilateur suffisamment intelligente pour suivre le code que vous écrivez.
Vous pouvez faire une implémentation de peut-être avec une langue qui a facilement des nuls. C++ en a un sous la forme de boost::optional<T>
. Rendre l'équivalent de null avec peut-être est très difficile. En particulier, si j'ai un Maybe<Just<T>>
, Je ne peux pas l'affecter à NULL (car un tel concept n'existe pas), tandis qu'un T**
Dans une langue avec NULL est très facile à attribuer à NULL. Cela force à utiliser Maybe<Maybe<T>>
, qui est totalement valide, mais vous obligera à faire beaucoup plus de vérifications pour utiliser cet objet.
Certaines langues fonctionnelles utilisent peut-être parce que NULL nécessite un comportement indéfini ou une manipulation d'exception, qui n'est pas un concept facile pour cartographier des syntaxes de langues fonctionnelles. Peut-être remplir beaucoup mieux le rôle dans de telles situations fonctionnelles, mais dans les langues de procédure, NULL est roi. Ce n'est pas une question de droit et de tort, juste une question de ce qui facilite la distinction à l'ordinateur de faire ce que vous voulez faire.
Je pense que c'est parce que la programmation fonctionnelle est très préoccupée par des types , en particulier des types combinant d'autres types (tuples, fonctions de premier ordre, monads, etc. ) que la programmation axée sur les objets (ou au moins initialement l'a fait).
Versions modernes des langages de programmation, je pense que vous parlez de (C++, C #, Java) sont tous basés sur des langues qui n'avaient aucune forme de programmation générique (C, C # 1.0, Java = 1). Sans cela, vous pouvez toujours cuire une sorte de différence entre des objets nullables et non nullables dans la langue (comme les références C++, qui ne peuvent pas être null
, mais sont également limitées), mais il est également limité beaucoup moins naturel.