web-dev-qa-db-fra.com

Meilleure explication pour les langues sans null

De temps en temps, lorsque les programmeurs se plaignent d'erreurs/exceptions nulles, quelqu'un demande ce que nous faisons sans null.

J'ai une idée de base de la fraîcheur des types d'options, mais je n'ai ni les connaissances ni les compétences linguistiques pour l'exprimer au mieux. Qu'est-ce qu'une explication grande de ce qui suit est écrite d'une manière accessible au programmeur moyen vers qui nous pourrions diriger cette personne?

  • Le caractère indésirable d'avoir des références/pointeurs soit nullable par défaut
  • Fonctionnement des types d’options, y compris stratégies pour faciliter la vérification des cas nuls tels que
    • correspondance de modèle et
    • compréhensions monadiques
  • Solution alternative telle que message eating néant
  • (autres aspects que j'ai manqués)
221
Roman A. Taycher

Je pense que le résumé succinct de la raison pour laquelle null est indésirable est que les états sans signification ne devraient pas être représentables.

Supposons que je modélise une porte. Il peut être dans l'un des trois états suivants: ouvert, fermé mais déverrouillé, fermé et verrouillé. Maintenant, je pourrais le modeler dans le sens de

class Door
    private bool isShut
    private bool isLocked

et il est clair comment mapper mes trois états dans ces deux variables booléennes. Mais cela laisse un quatrième état indésirable disponible: isShut==false && isLocked==true. Parce que les types que j'ai choisis comme représentation admettent cet état, je dois faire un effort mental pour que la classe ne passe jamais dans cet état (peut-être en codant explicitement un invariant). En revanche, si j’utilisais un langage avec des types de données algébriques ou des énumérations vérifiées, cela me permettait de définir

type DoorState =
    | Open | ShutAndUnlocked | ShutAndLocked

alors je pourrais définir

class Door
    private DoorState state

et il n'y a plus de soucis. Le système de types s'assurera qu'il n'y a que trois états possibles pour une instance de class Door. C'est à quoi les systèmes de types réussissent: éliminer explicitement toute une classe d'erreurs au moment de la compilation.

Le problème avec null est que chaque type de référence obtient cet état supplémentaire dans son espace généralement indésirable. Une variable string peut être une séquence de caractères quelconque, ou bien cette valeur extra folle null qui ne correspond pas à mon domaine problématique. Un objet Triangle a trois Points, qui ont eux-mêmes les valeurs X et Y, mais malheureusement, le Points ou le Triangle lui-même pourrait être cette valeur nulle insensée qui n'a pas de sens pour le domaine graphique dans lequel je travaille. Etc.

Lorsque vous avez l'intention de modéliser une valeur éventuellement non existante, vous devez vous y inscrire explicitement. Si j'ai l'intention de modéliser des personnes, c'est que chaque Person a un FirstName et un LastName, mais que seules certaines personnes ont MiddleNames, alors j'aimerais dire quelque chose comme

class Person
    private string FirstName
    private Option<string> MiddleName
    private string LastName

string ici est supposé être un type non nullable. Ensuite, il n’ya pas d’invariants difficiles à établir et pas de NullReferenceExceptions inattendue lorsque vous essayez de calculer la longueur du nom d’une personne. Le système de types garantit que tout code traitant des comptes MiddleName donne la possibilité d'être None, tandis que tout code traitant de FirstName peut supposer en toute sécurité qu'il existe une valeur .

Ainsi, par exemple, en utilisant le type ci-dessus, nous pourrions créer cette fonction idiote:

let TotalNumCharsInPersonsName(p:Person) =
    let middleLen = match p.MiddleName with
                    | None -> 0
                    | Some(s) -> s.Length
    p.FirstName.Length + middleLen + p.LastName.Length

sans soucis. En revanche, dans un langage avec des références nullables pour des types tels que string, supposant alors

class Person
    private string FirstName
    private string MiddleName
    private string LastName

vous finissez par créer des choses comme

let TotalNumCharsInPersonsName(p:Person) =
    p.FirstName.Length + p.MiddleName.Length + p.LastName.Length

qui explose si l'objet Personne entrant n'a pas l'invariant de tout ce qui n'est pas nul, ou

let TotalNumCharsInPersonsName(p:Person) =
    (if p.FirstName=null then 0 else p.FirstName.Length)
    + (if p.MiddleName=null then 0 else p.MiddleName.Length)
    + (if p.LastName=null then 0 else p.LastName.Length)

ou peut-être

let TotalNumCharsInPersonsName(p:Person) =
    p.FirstName.Length
    + (if p.MiddleName=null then 0 else p.MiddleName.Length)
    + p.LastName.Length

en supposant que p s'assure que le premier/le dernier sont présents, mais que le milieu peut être nul, ou peut-être effectuez-vous des vérifications qui génèrent différents types d'exceptions ou qui sait quoi. Tous ces choix d'implémentation loufoques et toutes ces choses à penser surgissent parce qu'il y a cette valeur représentable stupide que vous ne voulez ni dont vous n'avez pas besoin.

Null ajoute généralement une complexité inutile. La complexité est l’ennemi de tous les logiciels et vous devez vous efforcer de réduire cette complexité lorsque cela est raisonnable.

(Notez bien que même ces exemples simples sont plus complexes. Même si un FirstName ne peut pas être null, un string peut représenter "" (Le string), qui n'est probablement pas non plus un nom de personne que nous avons l'intention de modéliser. Ainsi, même avec des chaînes non nullables, il se peut que nous "représentions des valeurs sans signification". soit via des invariants et du code conditionnel au moment de l'exécution, soit en utilisant le système de types (par exemple, pour avoir un type NonEmptyString.). Ce dernier est peut-être déconseillé (les "bons" types sont souvent "fermés" sur un ensemble de Les opérations courantes, par exemple NonEmptyString, ne sont pas fermées sur .SubString(0,0)), mais montrent plus de points dans l'espace de conception. il sera très facile de se débarrasser de la complexité et de toute autre complexité qui est intrinsèquement plus difficile à éliminer. La clé de ce sujet est que dans presque chaque type système, le changement de "nullable refe La référence par défaut à "références non nullables par défaut" est presque toujours un simple changement qui améliore considérablement le système de types pour lutter contre la complexité et éliminer certains types d’erreurs et d’états sans signification. Il est donc assez fou que tant de langues répètent cette erreur encore et encore.)

426
Brian

La bonne chose à propos des types d’options n’est pas qu’ils sont facultatifs. C'est que tous les autres types ne le sont pas.

Parfois , nous devons pouvoir représenter une sorte d'état "nul". Parfois, nous devons représenter une option "aucune valeur" ainsi que les autres valeurs possibles qu'une variable peut prendre. Donc, un langage qui interdit totalement cela va être un peu handicapé.

Mais souvent , nous n'en avons pas besoin, et permettant une telle L'état "null" ne fait qu'engendrer ambiguïté et confusion: chaque fois que j'accède à une variable de type référence dans .NET, je dois tenir compte du fait que elle peut être nulle .

Souvent, il ne sera jamais réellement , car le programmeur structure le code de manière à ce que cela ne se produise jamais. Mais le compilateur ne peut pas vérifier cela, et chaque fois que vous le voyez, vous devez vous demander: "cela peut-il être nul? Ai-je besoin de rechercher null ici?"

Idéalement, dans les nombreux cas où null n'a pas de sens, , cela ne devrait pas être autorisé .

C'est difficile à réaliser dans .NET, où presque tout peut être nul. Vous devez vous fier à l'auteur du code que vous appelez pour qu'il soit rigoureux et cohérent, et qu'il ait clairement documenté ce qui peut ou ne peut pas être nul, ou vous devez être paranoïaque et vérifier tout.

Cependant, si les types ne sont pas nullables par défaut , vous n'avez pas besoin de vérifier s'ils sont nuls ou non. Vous savez qu'ils ne peuvent jamais être nuls, car le vérificateur du compilateur/type les applique pour vous.

Et puis, nous avons juste besoin d’une porte dérobée pour les rares cas où nous faisons devons gérer un état nul. Ensuite, un type "option" peut être utilisé. Ensuite, nous permettons à null dans les cas où nous avons pris une décision consciente qu'il est nécessaire de pouvoir représenter le cas "aucune valeur", et dans tous les autres cas, nous savons que la valeur ne sera jamais nulle.

Comme d'autres l'ont mentionné, en C # ou Java par exemple, null peut signifier l'une des deux choses suivantes:

  1. la variable n'est pas initialisée. Cela devrait, idéalement, jamais arriver. Une variable ne devrait pas exister à moins d'être initialisée.
  2. la variable contient des données "facultatives": elle doit pouvoir représenter le cas où il n'y a pas de données . C'est parfois nécessaire. Peut-être essayez-vous de trouver un objet dans une liste sans savoir à l'avance s'il existe ou non. Ensuite, nous devons pouvoir représenter le fait "qu'aucun objet n'a été trouvé".

La deuxième signification doit être préservée, mais la première doit être entièrement éliminée. Et même le second sens ne devrait pas être la valeur par défaut. C'est quelque chose que nous pouvons choisir si et quand nous en avons besoin . Mais lorsque nous n’avons pas besoin que quelque chose soit optionnel, nous voulons que le vérificateur de type garantisse qu’il ne sera jamais nul.

63
jalf

Jusqu'à présent, toutes les réponses se concentrent sur les raisons pour lesquelles null est une mauvaise chose et sur la façon dont il est pratique si un langage peut garantir que certaines valeurs jamais seront nulles.

Ils suggèrent ensuite que ce serait une très bonne idée si vous imposiez la non-nullabilité pour toutes les valeurs , ce qui peut être fait si vous ajoutez un Des concepts tels que Option ou Maybe représentent des types qui n'ont pas toujours une valeur définie. C'est l'approche adoptée par Haskell.

C'est du bon matos! Mais cela n’empêche pas l’utilisation de types explicitement nullables/non nuls pour obtenir le même effet. Pourquoi, alors, Option est-il encore une bonne chose? Après tout, Scala supporte les valeurs nullables (est a pour à, de sorte que cela puisse fonctionner avec Java bibliothèques) mais supporte également Options.

Q. Quels sont donc les avantages, au-delà de la possibilité de supprimer complètement les valeurs nulles d'une langue?

A. Composition

Si vous faites une traduction naïve à partir de code null-aware

def fullNameLength(p:Person) = {
  val middleLen =
    if (null == p.middleName)
      p.middleName.length
    else
      0
  p.firstName.length + middleLen + p.lastName.length
}

au code à option

def fullNameLength(p:Person) = {
  val middleLen = p.middleName match {
    case Some(x) => x.length
    case _ => 0
  }
  p.firstName.length + middleLen + p.lastName.length
}

il n'y a pas beaucoup de différence! Mais c’est aussi une façon terrible d’utiliser Options ... Cette approche est beaucoup plus propre:

def fullNameLength(p:Person) = {
  val middleLen = p.middleName map {_.length} getOrElse 0
  p.firstName.length + middleLen + p.lastName.length
}

Ou même:

def fullNameLength(p:Person) =       
  p.firstName.length +
  p.middleName.map{length}.getOrElse(0) +
  p.lastName.length

Lorsque vous commencez à utiliser la liste d'options, la situation s'améliore encore. Imaginons que la liste people soit elle-même facultative:

people flatMap(_ find (_.firstName == "joe")) map (fullNameLength)

Comment cela marche-t-il?

//convert an Option[List[Person]] to an Option[S]
//where the function f takes a List[Person] and returns an S
people map f

//find a person named "Joe" in a List[Person].
//returns Some[Person], or None if "Joe" isn't in the list
validPeopleList find (_.firstName == "joe")

//returns None if people is None
//Some(None) if people is valid but doesn't contain Joe
//Some[Some[Person]] if Joe is found
people map (_ find (_.firstName == "joe")) 

//flatten it to return None if people is None or Joe isn't found
//Some[Person] if Joe is found
people flatMap (_ find (_.firstName == "joe")) 

//return Some(length) if the list isn't None and Joe is found
//otherwise return None
people flatMap (_ find (_.firstName == "joe")) map (fullNameLength)

Le code correspondant avec des contrôles nuls (ou même elvis?: Opérateurs) serait péniblement long. Le vrai truc ici est l’opération flatMap, qui permet une compréhension imbriquée des options et des collections d’une manière que les valeurs pouvant être annulées ne peuvent jamais atteindre.

44
Kevin Wright

Comme les gens semblent le manquer, null est ambigu.

La date de naissance d'Alice est null. Qu'est-ce que ça veut dire?

La date de décès de Bob est null. Qu'est-ce que ça veut dire?

Une interprétation "raisonnable" pourrait être que la date de naissance d'Alice existe mais qu'elle est inconnue, alors que la date de décès de Bob n'existe pas (Bob est toujours en vie). Mais pourquoi en sommes-nous arrivés à des réponses différentes?


Un autre problème: null est un cas Edge.

  • Est-ce que null = null?
  • Est-ce que nan = nan?
  • Est-ce que inf = inf?
  • Est-ce que +0 = -0?
  • Est-ce que +0/0 = -0/0?

Les réponses sont généralement "oui", "non", "oui", "oui", "non", "oui" respectivement. Les "mathématiciens" fous appellent NaN "nullité" et disent que cela se compare à lui-même. SQL traite les valeurs nulles comme n'étant égales à rien (elles se comportent donc comme des NaN). On se demande ce qui se passe lorsque vous essayez de stocker ±, ± 0 et les NaN dans la même colonne de base de données (il y a 253 NaN, dont la moitié sont "négatives").

Pour aggraver les choses, les bases de données diffèrent quant à la manière dont elles traitent NULL, et la plupart d'entre elles ne sont pas cohérentes (voir Gestion de NULL dans SQLite pour un aperçu). C'est assez horrible.


Et maintenant pour l'histoire obligatoire:

J'ai récemment conçu une table de base de données (sqlite3) avec cinq colonnes a NOT NULL, b, id_a, id_b NOT NULL, timestamp. Comme il s'agit d'un schéma générique conçu pour résoudre un problème générique pour des applications relativement arbitraires, il existe deux contraintes d'unicité:

UNIQUE(a, b, id_a)
UNIQUE(a, b, id_b)

id_a N'existe que pour la compatibilité avec une conception d'application existante (en partie parce que je n'ai pas trouvé de meilleure solution) et n'est pas utilisé dans la nouvelle application. En raison de la manière dont NULL fonctionne en SQL, je peux insérer (1, 2, NULL, 3, t) Et (1, 2, NULL, 4, t) Sans violer la première contrainte d'unicité (parce que (1, 2, NULL) != (1, 2, NULL)).

Cela fonctionne spécifiquement en raison de la manière dont NULL agit dans une contrainte d'unicité sur la plupart des bases de données (il est donc vraisemblable qu'il est plus facile de modéliser des situations "réelles", par exemple, deux personnes ne peuvent pas avoir le même numéro de sécurité sociale, mais toutes les personnes n'en ont pas un).


FWIW, sans d'abord appeler un comportement indéfini, les références C++ ne peuvent pas "pointer sur" null et il n'est pas possible de construire une classe avec des variables de membre de référence non initialisées (si une exception est levée, la construction échoue).

Note de bas de page: Parfois, vous voudrez peut-être des pointeurs mutuellement exclusifs (c'est-à-dire qu'un seul d'entre eux peut être non-NULL), par exemple. dans un iOS hypothétique type DialogState = NotShown | ShowingActionSheet UIActionSheet | ShowingAlertView UIAlertView | Dismissed. Au lieu de cela, je suis obligé de faire des choses comme assert((bool)actionSheet + (bool)alertView == 1).

38
tc.

Le caractère indésirable d'avoir des références/des pointeurs sera nullable par défaut.

Je ne pense pas que ce soit le problème principal avec les valeurs NULL, le problème principal est qu'elles peuvent signifier deux choses:

  1. La référence/le pointeur n'est pas initialisé: le problème ici est identique à la mutabilité en général. D'une part, il est plus difficile d'analyser votre code.
  2. La variable étant nulle signifie en réalité quelque chose: c'est le cas que les types d'options formalisent réellement.

Les langues qui prennent en charge les types d’option interdisent ou découragent également l’utilisation de variables non initialisées.

Fonctionnement des types d’options, y compris stratégies pour faciliter la vérification des cas nuls tels que la correspondance de modèle.

Pour être efficaces, les types d’option doivent être pris en charge directement dans la langue. Sinon, il faut beaucoup de code de plaque de chaudière pour les simuler. La correspondance de modèle et l'inférence de type sont deux fonctionnalités clés du langage rendant les types d'option faciles à utiliser. Par exemple:

En F #:

//first we create the option list, and then filter out all None Option types and 
//map all Some Option types to their values.  See how type-inference shines.
let optionList = [Some(1); Some(2); None; Some(3); None]
optionList |> List.choose id //evaluates to [1;2;3]

//here is a simple pattern-matching example
//which prints "1;2;None;3;None;".
//notice how value is extracted from op during the match
optionList 
|> List.iter (function Some(value) -> printf "%i;" value | None -> printf "None;")

Cependant, dans un langage comme Java sans prise en charge directe des types Option, nous aurions quelque chose comme:

//here we perform the same filter/map operation as in the F# example.
List<Option<Integer>> optionList = Arrays.asList(new Some<Integer>(1),new Some<Integer>(2),new None<Integer>(),new Some<Integer>(3),new None<Integer>());
List<Integer> filteredList = new ArrayList<Integer>();
for(Option<Integer> op : list)
    if(op instanceof Some)
        filteredList.add(((Some<Integer>)op).getValue());

Solution alternative telle que message en mangeant rien

Le message "manger néant" d'Objective-C n'est pas tant une solution qu'une tentative d'alléger le mal de tête de la vérification à zéro. Fondamentalement, au lieu de lancer une exception d'exécution lors de la tentative d'appeler une méthode sur un objet null, l'expression est évaluée à null elle-même. Suspendre l'incrédulité, c'est comme si chaque méthode d'instance commençait par if (this == null) return null;. Mais ensuite, il y a une perte d'information: vous ne savez pas si la méthode a renvoyé la valeur null car il s'agit d'une valeur de retour valide ou parce que l'objet est en fait null. Cela ressemble beaucoup à une déglutition exceptionnelle et ne fait aucun progrès en ce qui concerne les problèmes liés à null précédemment décrits.

16
Stephen Swensen

L'Assemblée nous a apporté des adresses également connues sous le nom de pointeurs non typés. C les mappa directement en tant que pointeurs typés mais introduisit la valeur null d'ALGOL en tant que valeur de pointeur unique, compatible avec tous les pointeurs typés. Le gros problème avec null en C est que puisque chaque pointeur peut être nul, on ne peut jamais utiliser un pointeur en toute sécurité sans vérification manuelle.

Dans les langages de niveau supérieur, avoir la valeur null est gênant, car il véhicule deux notions distinctes:

  • Dire que quelque chose est non défini .
  • Dire que quelque chose est optionnel .

Avoir des variables non définies est pratiquement inutile et donne lieu à un comportement non défini à chaque fois qu'elles se produisent. Je suppose que tout le monde conviendra qu'il faut éviter à tout prix d'avoir des choses non définies.

Le second cas est optionnel et est mieux fourni explicitement, par exemple avec un type d'option .


Supposons que nous sommes dans une entreprise de transport et que nous devons créer une application pour créer un horaire pour nos chauffeurs. Pour chaque conducteur, nous stockons quelques informations telles que: les permis de conduire dont ils disposent et le numéro de téléphone à appeler en cas d'urgence.

En C on aurait pu:

struct PhoneNumber { ... };
struct MotorbikeLicence { ... };
struct CarLicence { ... };
struct TruckLicence { ... };

struct Driver {
  char name[32]; /* Null terminated */
  struct PhoneNumber * emergency_phone_number;
  struct MotorbikeLicence * motorbike_licence;
  struct CarLicence * car_licence;
  struct TruckLicence * truck_licence;
};

Comme vous le constatez, lors de tout traitement sur notre liste de pilotes, nous devrons vérifier les pointeurs nuls. Le compilateur ne vous aidera pas, la sécurité du programme repose sur vos épaules.

Dans OCaml, le même code ressemblerait à ceci:

type phone_number = { ... }
type motorbike_licence = { ... }
type car_licence = { ... }
type truck_licence = { ... }

type driver = {
  name: string;
  emergency_phone_number: phone_number option;
  motorbike_licence: motorbike_licence option;
  car_licence: car_licence option;
  truck_licence: truck_licence option;
}

Disons maintenant que nous voulons imprimer les noms de tous les conducteurs avec leur numéro de permis de conduire.

En C:

#include <stdio.h>

void print_driver_with_truck_licence_number(struct Driver * driver) {
  /* Check may be redundant but better be safe than sorry */
  if (driver != NULL) {
    printf("driver %s has ", driver->name);
    if (driver->truck_licence != NULL) {
      printf("truck licence %04d-%04d-%08d\n",
        driver->truck_licence->area_code
        driver->truck_licence->year
        driver->truck_licence->num_in_year);
    } else {
      printf("no truck licence\n");
    }
  }
}

void print_drivers_with_truck_licence_numbers(struct Driver ** drivers, int nb) {
  if (drivers != NULL && nb >= 0) {
    int i;
    for (i = 0; i < nb; ++i) {
      struct Driver * driver = drivers[i];
      if (driver) {
        print_driver_with_truck_licence_number(driver);
      } else {
        /* Huh ? We got a null inside the array, meaning it probably got
           corrupt somehow, what do we do ? Ignore ? Assert ? */
      }
    }
  } else {
    /* Caller provided us with erroneous input, what do we do ?
       Ignore ? Assert ? */
  }
}

En OCaml, ce serait:

open Printf

(* Here we are guaranteed to have a driver instance *)
let print_driver_with_truck_licence_number driver =
  printf "driver %s has " driver.name;
  match driver.truck_licence with
    | None ->
        printf "no truck licence\n"
    | Some licence ->
        (* Here we are guaranteed to have a licence *)
        printf "truck licence %04d-%04d-%08d\n"
          licence.area_code
          licence.year
          licence.num_in_year

(* Here we are guaranteed to have a valid list of drivers *)
let print_drivers_with_truck_licence_numbers drivers =
  List.iter print_driver_with_truck_licence_number drivers

Comme vous pouvez le constater dans cet exemple trivial, la version sécurisée n’a rien de compliqué:

  • C'est terser.
  • Vous obtenez de bien meilleures garanties et aucun chèque nul n'est requis.
  • Le compilateur s’est assuré que vous avez correctement traité l’option

Alors que dans C, vous pourriez simplement avoir oublié un chèque nul et boum ...

Remarque: ces exemples de code n'ont pas été compilés, mais j'espère que vous avez eu les idées.

11
bltxd

Microsoft Research a un projet intéressant appelé

Spec #

C'est une extension C # avec un type non-null et un mécanisme permettant à de vérifier que vos objets ne sont pas nuls , bien que, à mon humble avis, l’application du principe de conception par contrat soit plus approprié et plus utile dans de nombreuses situations problématiques causées par null les références.

5
Jahan Zinedine

Venant de fond .NET, j'ai toujours pensé que null avait un point, son utilité. Jusqu'à ce que je connaisse les structures et à quel point il était facile de travailler avec elles en évitant beaucoup de code passe-partout. Tony Hoare parlant à QCon London en 2009, s'est excusé pour avoir inventé la référence nulle . Pour le citer:

Je l'appelle mon erreur d'un milliard de dollars. C’était l’invention de la référence null en 1965. À cette époque, je concevais le premier système de types complet pour les références dans un langage orienté objet (ALGOL W). Mon objectif était de veiller à ce que toute utilisation des références soit absolument sûre, avec une vérification automatique par le compilateur. Mais je ne pouvais pas résister à la tentation de mettre une référence nulle, tout simplement parce que c'était si facile à mettre en œuvre. Cela a entraîné d'innombrables erreurs, vulnérabilités et pannes système, qui ont probablement causé des millions de dollars de douleur et de dommages au cours des quarante dernières années. Ces dernières années, plusieurs analyseurs de programme tels que PREfix et PREfast de Microsoft ont été utilisés pour vérifier les références et émettre des avertissements s’ils risquaient d’être non nuls. Des langages de programmation plus récents tels que Spec # ont introduit des déclarations pour des références non nulles. C’est la solution que j’ai rejetée en 1965.

Voir aussi cette question chez les programmeurs

4
nawfal

Robert Nystrom propose un bel article ici:

http://journal.stuffwithstuff.com/2010/08/23/void-null-maybe-and-nothing/

décrivant son processus de pensée lors de l'ajout de la prise en charge de l'absence et de l'échec à son langage de programmation Magpie .

3
Corbin March

J'ai toujours considéré Null (ou nil) comme étant absence de valeur.

Parfois, vous le souhaitez, parfois non. Cela dépend du domaine sur lequel vous travaillez. Si l'absence est significative: pas de prénom, votre application peut agir en conséquence. Par ailleurs, si la valeur null ne doit pas être présente: le prénom est null, le développeur reçoit alors le proverbial appel téléphonique 2 heures du matin.

J'ai également vu du code surchargé et trop compliqué avec des vérifications nulles. Pour moi, cela signifie l'une des deux choses suivantes:
a) un bogue plus haut dans l'arbre d'application
b) conception mauvaise/incomplète

Du côté positif, Null est probablement l'une des notions les plus utiles pour vérifier si quelque chose est absent, et les langages sans le concept de null finiront par compliquer les choses de manière compliquée au moment de valider les données. Dans ce cas, si une nouvelle variable n'est pas initialisée, ladite langue définira généralement les variables sur une chaîne vide, sur 0 ou sur une collection vide. Cependant, si une chaîne vide ou une collection vide ou 0 sont valeurs valides pour votre application - alors vous avez un problème.

Parfois, cela a été contourné en inventant des valeurs spéciales/étranges pour les champs afin de représenter un état non initialisé. Mais alors que se passe-t-il lorsque la valeur spéciale est entrée par un utilisateur bien intentionné? Et n'entrons pas dans le pétrin que cela va engendrer des routines de validation des données. Si le langage prenait en charge le concept nul, toutes les préoccupations disparaîtraient.

1
Jon

Les langues de vecteurs peuvent parfois s'en tirer sans avoir un zéro.

Le vecteur vide sert dans ce cas comme un null typé.

0
Joshua