Je suis sûr que les concepteurs de langages tels que Java ou C # connaissaient des problèmes liés à l'existence de références null (voir Est-ce que les références null sont vraiment une mauvaise chose? ). De plus, implémenter un type d'option n'est pas vraiment beaucoup plus complexe que les références nulles.
Pourquoi ont-ils décidé de l'inclure de toute façon? Je suis certain que le manque de références nulles encouragerait (ou même forcerait) un code de meilleure qualité (en particulier une meilleure conception de bibliothèque) à la fois des créateurs de langage et des utilisateurs.
Est-ce simplement à cause du conservatisme - "d'autres langues l'ont, nous devons l'avoir aussi ..."?
language-design
null
Mrpyo
la source
la source
Réponses:
Déni de responsabilité: Étant donné que je ne connais personnellement aucun concepteur de langage, toute réponse que je vous donnerai sera spéculative.
De Tony Hoare lui-même:
L'accent est à moi.
Naturellement, cela ne lui semblait pas une mauvaise idée à l'époque. Il est probable que cela ait été en partie perpétué pour la même raison: si cela semblait être une bonne idée pour l'inventeur du tri rapide récompensé par le prix Turing, il n'est pas étonnant que beaucoup de gens ne comprennent toujours pas pourquoi c'est mal. Cela s'explique probablement aussi par le fait qu'il est pratique que les nouvelles langues soient similaires aux anciennes, à la fois pour des raisons de marketing et d'apprentissage. Exemple:
(Source: http://www.paulgraham.com/icad.html )
Et, bien sûr, C ++ a la valeur null car C a la valeur null et il n'est pas nécessaire d'entrer dans l'impact historique de C. Le type C # de J ++ remplacé, qui était l'implémentation de Java par Microsoft, et il a également remplacé le C ++ en tant que langage de choix pour le développement Windows, de sorte qu'il aurait pu l'obtenir de l'un ou l'autre.
EDIT Voici une autre citation de Hoare à considérer:
Encore une fois, soulignez le mien. Sun / Oracle et Microsoft sont des entreprises et l’argent est au cœur de toute entreprise. Les avantages pour eux d’avoir
null
pu l'emporter sur les inconvénients, ou bien ils ont peut-être eu une échéance trop serrée pour examiner pleinement la question. Voici un exemple d'erreur de langage différente qui s'est probablement produite à cause des délais:(Source: http://www.artima.com/intv/bloch13.html )
la source
null
son langage. Toute réponse à cette question sera spéculative sauf si elle provient d’un concepteur de langage. Je ne connais personne qui fréquente ce site à part Eric Lippert. La dernière partie est un hareng rouge pour de nombreuses raisons. La quantité de code tiers écrit au-dessus des API de MS et de Java dépasse évidemment la quantité de code contenue dans l'API elle-même. Donc, si vos clients veulentnull
, vous leur donneznull
. Vous supposez également qu'ils ont accepténull
leur coûte de l'argent.ICloneable
est pareillement cassé dans .NET; Malheureusement, c'est un endroit où les lacunes de Java n'ont pas été apprises à temps.Bien sûr.
Je ne suis pas d'accord! Les considérations de conception incluses dans les types de valeur nullable en C # 2 étaient complexes, controversées et difficiles. Ils ont pris les équipes de conception des langues et de l'exécution plusieurs mois de débat, de mise en œuvre de prototypes, etc., et en fait, la sémantique de la boxe annulable a été modifiée de manière très proche de l'envoi du C # 2.0, ce qui était très controversé.
Toute conception est un processus de choix parmi de nombreux objectifs incompatibles de manière subtile et flagrante; Je ne peux donner qu'un bref aperçu de quelques-uns des facteurs à prendre en compte:
L'orthogonalité des caractéristiques linguistiques est généralement considérée comme une bonne chose. C # a des types de valeur nullables, des types de valeur non nullables et des types de référence nullables. Les types de référence non nulles n'existent pas, ce qui rend le système de types non orthogonal.
La connaissance des utilisateurs existants de C, C ++ et Java est importante.
Une interopérabilité facile avec COM est importante.
Une interopérabilité facile avec tous les autres langages .NET est importante.
Une interopérabilité facile avec les bases de données est importante.
La cohérence de la sémantique est importante. si nous avons la référence TheKingOfFrance égale à null, cela signifie-t-il toujours "il n'y a pas de roi de France à l'heure actuelle", ou peut-il aussi signifier "Il existe bel et bien un roi de France; je ne sais tout simplement pas de qui il s'agit maintenant"? ou peut-il vouloir dire "l'idée même d'avoir un roi en France est absurde, alors ne posez même pas la question!"? Null peut signifier toutes ces choses et plus encore en C #, et tous ces concepts sont utiles.
Le coût de performance est important.
Être disposé à une analyse statique est important.
La cohérence du système de types est importante; pouvons - nous toujours savoir qui est une référence non annulable jamais en aucun cas observés invalide? Qu'en est-il dans le constructeur d'un objet avec un champ de type référence non nullable? Qu'en est-il dans le finaliseur d'un tel objet, où l'objet est finalisé parce que le code qui était censé remplir la référence renvoyait une exception ? Un système de type qui vous ment sur ses garanties est dangereux.
Et que dire de la cohérence de la sémantique? Les valeurs nulles se propagent lorsqu'elles sont utilisées, mais les références nulles émettent des exceptions lorsqu'elles sont utilisées. C'est incohérent Cette incohérence est-elle justifiée par un avantage?
Pouvons-nous implémenter la fonctionnalité sans en casser d'autres? Quelles autres fonctionnalités futures possibles la fonctionnalité exclut-elle?
Vous allez faire la guerre à l'armée que vous avez, pas à celle que vous souhaitez. N'oubliez pas que C # 1.0 n'avait pas de génériques. Par conséquent, le fait de parler
Maybe<T>
d'alternative est un non-débutant. .NET aurait-il dû glisser depuis deux ans pendant que l'équipe d'exécution ajoutait des génériques, uniquement pour éliminer les références nulles?Qu'en est-il de la cohérence du système de types? Vous pouvez dire
Nullable<T>
pour n'importe quel type de valeur - non, attendez, c'est un mensonge. Tu ne peux pas direNullable<Nullable<T>>
. Devriez-vous pouvoir? Si oui, quelle est la sémantique souhaitée? Vaut-il la peine de donner à tout le système de types un cas particulier juste pour cette fonctionnalité?Etc. Ces décisions sont complexes.
la source
catches
font rien. Je pense qu'il est préférable de laisser le système exploser au lieu de continuer à fonctionner dans un état éventuellement non valide.Null sert un objectif très valable de représenter un manque de valeur.
Je dirai que je suis la personne la plus éloquente que je connaisse au sujet des abus de null et de tous les maux de tête et de souffrances qu’ils peuvent causer, en particulier lorsqu’ils sont utilisés généreusement.
Ma position personnelle est que les personnes peuvent utiliser des valeurs nulles uniquement lorsqu'elles peuvent justifier qu'elles sont nécessaires et appropriées.
Exemple justifiant des valeurs nulles:
Date de décès est généralement un champ nullable. Il y a trois situations possibles avec la date du décès. Soit la personne est décédée et la date est connue, la personne est décédée et la date est inconnue, soit la personne n’est pas morte et, par conséquent, la date du décès n’existe pas.
Date of Death est également un champ DateTime et n'a pas de valeur "unknown" ou "empty". La date par défaut qui apparaît lorsque vous créez une nouvelle date / heure varie en fonction de la langue utilisée, mais il est techniquement possible que cette personne décède à ce moment-là et qu'elle soit considérée comme votre "valeur vide" si vous deviez utilisez la date par défaut.
Les données devraient représenter la situation correctement.
La personne est morte la date du décès est connue (3/9/1984)
Simple, '3/9/1984'
La personne est morte date inconnue
Alors quoi de mieux? Null , '0/0/0000' ou '01 / 01/1869 '(ou quelle que soit votre valeur par défaut est?)
Personne n'est pas la date morte du décès n'est pas applicable
Alors quoi de mieux? Null , '0/0/0000' ou '01 / 01/1869 '(ou quelle que soit votre valeur par défaut est?)
Alors réfléchissons à chaque valeur ...
Le fait est parfois vous ne doivent représenter rien et que parfois un type de variable fonctionne bien pour cela, mais les types souvent variables doivent être en mesure de représenter rien.
Si je n'ai pas de pommes, j'ai 0 pommes, mais que se passe-t-il si je ne sais pas combien de pommes j'ai?
Bien entendu, null est utilisé de manière abusive et potentiellement dangereux, mais il est parfois nécessaire. Ce n'est que par défaut dans de nombreux cas car jusqu'à ce que je fournisse une valeur, l'absence d'une valeur et quelque chose doit la représenter. (Nul)
la source
Null serves a very valid purpose of representing a lack of value.
UnOption
ouMaybe
type sert ce but très valable sans contourner le système de types.NOT NULL
. Ma question n'était cependant pas liée au SGBDR ...Person
type a unstate
champ de typeState
, qui est une union discriminée deAlive
etDead(dateOfDeath : Date)
.Je n'irais pas aussi loin que "d'autres langues l'ont, nous devons l'avoir aussi ...", comme si c'était comme suivre les Jones. Une caractéristique clé de tout nouveau langage est la capacité d'interopérer avec des bibliothèques existantes dans d'autres langages (lire: C). Puisque C a des pointeurs nuls, la couche d'interopérabilité a nécessairement besoin du concept de zéro (ou de tout autre équivalent "n'existe pas" qui explose lorsque vous l'utilisez).
Le concepteur de langage aurait pu choisir d’utiliser les types d’option et de vous obliger à gérer le chemin nul où les choses puissent être nulles. Et cela conduirait presque certainement à moins de bugs.
Mais (surtout pour Java et C # en raison du moment choisi pour leur introduction et de leur public cible), l’utilisation des types d’options pour cette couche d’interopérabilité aurait probablement nui, voire torpillé, leur adoption. Soit le type d’option est transmis jusqu’à la fin, gênant énormément les programmeurs C ++ du milieu à la fin des années 90 - ou la couche d’interopérabilité jetterait des exceptions lorsqu’elle rencontrait des valeurs nulles, énervant énormément les programmeurs C ++ du milieu à la fin des années 1990. ..
la source
Option
dans Scala, ce qui est aussi simple queval x = Option(possiblyNullReference)
. En pratique, il ne faut pas longtemps pour que les gens voient les avantages d’unOption
.Match
méthode qui prend les délégués en argument. Ensuite, vous transmettez les expressions lambda àMatch
(points de bonus pour l’utilisation d’arguments nommés) etMatch
appelez celle de droite.Tout d'abord, je pense que nous pouvons tous convenir qu'un concept de nullité est nécessaire. Il y a des situations où nous devons représenter l' absence d'information.
Autoriser les
null
références (et les pointeurs) n’est qu’une des implémentations de ce concept, et peut-être même le plus populaire, bien qu’il soit connu pour avoir des problèmes: C, Java, Python, Ruby, PHP, JavaScript,… utilisent tous le mêmenull
.Pourquoi ? Eh bien, quelle est l'alternative?
Dans les langages fonctionnels tels que Haskell, vous avez le type
Option
ouMaybe
; cependant, ceux-ci sont construits sur:Les versions C, Java, Python, Ruby ou PHP d’origine prennent-elles en charge l’une ou l’autre de ces fonctionnalités? Les génériques défectueux de Java sont récents dans l'histoire du langage et je doute que les autres les mettent même en œuvre.
Voilà.
null
est facile, les types de données algébriques paramétriques sont plus difficiles. Les gens ont opté pour la solution la plus simple.la source
Object
" ne reconnaissent pas que les applications pratiques ont besoin d'objets mutables non partagés et d'objets immuables partageables (qui doivent tous deux se comporter comme des valeurs), ainsi que d'objets partageables et non échangeables. entités. L'utilisation d'un seulObject
type pour tout n'élimine pas le besoin de telles distinctions; cela rend simplement plus difficile de les utiliser correctement.Null / nil / none n'est pas mauvais en soi.
Si vous regardez son célèbre discours trompeur "L'erreur d'un milliard de dollars", Tony Hoare explique à quel point le fait d'autoriser une variable à conserver la valeur null était une énorme erreur. L'alternative - à l' aide des options - ne pas en fait se débarrasser des références nulles. Au lieu de cela, il vous permet de spécifier quelles variables sont autorisées à contenir null et celles qui ne le sont pas.
En fait, avec les langages modernes qui implémentent le traitement des exceptions, les erreurs de déréférencement nul ne sont pas différentes des autres exceptions: vous les trouvez, vous les corrigez. Certaines alternatives aux références nulles (le modèle Null Object, par exemple) cachent les erreurs, ce qui entraîne l'échec silencieux des choses jusque beaucoup plus tard. À mon avis, il vaut beaucoup mieux échouer rapidement .
La question est donc de savoir pourquoi les langues ne parviennent pas à implémenter Options. En fait, le langage sans doute le plus populaire de tous les temps, le C ++, est capable de définir des variables d'objet qui ne peuvent pas être attribuées
NULL
. C’est une solution au "problème nul" que Tony Hoare a mentionné dans son discours. Pourquoi le prochain langage typé le plus populaire, Java, ne l’a-t-il pas? On pourrait se demander pourquoi il a tant de défauts en général, en particulier dans son système de types. Je ne pense pas que vous puissiez vraiment dire que les langues font systématiquement cette erreur. Certains le font, d'autres pas.la source
null
.null
s'agit du seul paramètre par défaut raisonnable. Cependant, les références utilisées pour encapsuler une valeur pourraient avoir un comportement par défaut raisonnable dans les cas où un type aurait ou pourrait construire une instance par défaut raisonnable. De nombreux aspects du comportement des références dépendent de savoir si et comment elles encapsulent de la valeur, mais ...foo
détient la seule référence à unint[]
conteneur{1,2,3}
et que le code souhaitefoo
conserver une référence à unint[]
conteneur{2,2,3}
, le moyen le plus rapide d'y parvenir serait d'incrémenterfoo[0]
. Si le code veut faire savoir à une méthode qu'ellefoo
tient{1,2,3}
, l'autre méthode ne modifiera pas le tableau, ni ne persistera une référence au-delà du point où vousfoo
voudriez le modifier, le moyen le plus rapide d'y parvenir serait de passer une référence au tableau. Si Java avait un type de "référence éphémère en lecture seule", alors ...Parce que les langages de programmation sont généralement conçus pour être pratiques plutôt que techniquement corrects. Le fait est que les
null
états sont fréquents en raison de données incorrectes ou manquantes ou d'un état qui n'a pas encore été décidé. Les solutions techniquement supérieures sont toutes plus lourdes que de simplement autoriser les états nuls et d’absorber le fait que les programmeurs font des erreurs.Par exemple, si je veux écrire un script simple qui fonctionne avec un fichier, je peux écrire un pseudo-code comme:
et cela échouera simplement si joebloggs.txt n'existe pas. Le problème, c’est que, pour les scripts simples, c’est probablement correct et que, dans de nombreuses situations de code plus complexe, je sais qu’il existe et que l’échec ne se produira pas. Me forcer à vérifier me fait perdre mon temps. Les alternatives plus sûres atteignent leur sécurité en m'obligeant à gérer correctement l'état de défaillance potentiel, mais souvent je ne veux pas le faire, je veux juste continuer.
la source
for line in file
) et lève une exception de référence null sans signification, ce qui est acceptable pour un programme aussi simple, mais pose de véritables problèmes de débogage dans des systèmes beaucoup plus complexes. Si la valeur null n'existait pas, le concepteur "openfile" ne pourrait pas commettre cette erreur.let file = something(...).unwrap()
. En fonction de votre POV, c’est un moyen simple de ne pas gérer les erreurs ou les assertions succinctes qui ne peuvent pas être nulles. Le temps perdu est minime et vous gagnez du temps ailleurs car vous n'avez pas à déterminer si quelque chose peut être nul. Un autre avantage (qui peut à lui seul valoir l'appel supplémentaire) est que vous ignorez explicitement le cas d'erreur. quand il échoue, il ne fait aucun doute que ce qui a mal tourné et où le correctif doit aller.if file exists { open file }
souffre d'une situation de compétition . Le seul moyen fiable de savoir si l'ouverture d'un fichier réussira est d'essayer de l'ouvrir.Il existe des utilisations claires et pratiques du pointeur
NULL
(ounil
, ouNil
, ounull
,Nothing
ou de quelque manière que ce soit appelé dans votre langue préférée).Pour les langues qui ne disposent pas d'un système d'exception (par exemple C), un pointeur NULL peut être utilisé comme marque d'erreur lorsqu'un pointeur doit être renvoyé. Par exemple:
Ici, un
NULL
retour demalloc(3)
est utilisé comme marqueur d'échec.Lorsqu'il est utilisé dans les arguments de méthode / fonction, il peut indiquer use default pour l'argument ou ignorer l'argument de sortie. Exemple ci-dessous.
Même pour les langues avec mécanisme d'exception, un pointeur null peut être utilisé comme indication d'erreur logicielle (c'est-à-dire d'erreurs récupérables), en particulier lorsque la gestion des exceptions est coûteuse (par exemple, Objective-C):
Ici, l'erreur logicielle ne provoque pas le blocage du programme s'il n'est pas intercepté. Ceci élimine le try-catch fou comme Java et permet de mieux contrôler le déroulement du programme, car les erreurs logicielles ne s'interrompent pas (et les quelques exceptions matérielles restantes ne sont généralement pas récupérables et ne sont pas récupérées)
la source
null
de celles qui devraient. Par exemple, si je veux un nouveau type contenant 5 valeurs en Java, je pourrais utiliser une énumération, mais ce que je reçois est un type pouvant contenir 6 valeurs (les 5 que je voulais +null
). C'est une faille dans le système de types.Null
ne peut se voir attribuer une signification que lorsque les valeurs d'un type ne contiennent aucune donnée (par exemple, des valeurs enum). Dès que vos valeurs sont plus compliquées (par exemple, une structure),null
vous ne pouvez attribuer une signification qui ait du sens pour ce type. Il n'y a aucun moyen d'utiliser unnull
comme structure ou une liste. Et, encore une fois, le problème de l’utilisation ennull
tant que signal d’erreur est que nous ne pouvons pas déterminer ce qui peut renvoyer null ni accepter null. N'importe quelle variable de votre programme pourrait être, ànull
moins que vous ne deveniez extrêmement méticuleux, de vérifier chacune d'entre ellesnull
avant chaque utilisation, ce que personne ne fait.null
comme une valeur par défaut utilisable (par exemple, avoir la valeur par défaut destring
se comporter comme une chaîne vide, comme c'était le cas dans le précédent modèle à objets communs). Tout ce qui aurait été nécessaire aurait été d'utiliser les languescall
plutôt que d'callvirt
appeler des membres non virtuels.Il y a deux problèmes liés, mais légèrement différents:
null
exister du tout? Ou devriez-vous toujours utiliserMaybe<T>
où null est utile?Toutes les références doivent-elles être nulles? Si non, quelle devrait être la valeur par défaut?
Avoir à déclarer explicitement les types de référence nullable comme
string?
ou similaires éviterait la plupart des problèmes (mais pas tous)null
, sans être trop différent de ce à quoi les programmeurs sont habitués.Je suis au moins d’accord avec vous pour dire que toutes les références ne devraient pas pouvoir être annulées. Mais éviter la nullité n’est pas sans complexité:
.NET initialise tous les champs
default<T>
avant d’être accessibles en premier par le code managé. Cela signifie que pour les types de référence dont vous avez besoinnull
ou quelque chose d'équivalent, les types de valeur peuvent être initialisés à une sorte de zéro sans exécuter de code. Bien que ces deux solutions présentent des inconvénients graves, la simplicité d’default
initialisation a peut-être dépassé ces inconvénients.Par exemple, vous pouvez contourner ce problème en imposant l'initialisation des champs avant d'exposer le
this
pointeur au code géré. Spec # a choisi cette voie en utilisant une syntaxe différente de celle du constructeur par rapport à C #.Cela est plus difficile pour les champs statiques , à moins que vous ne posiez de fortes restrictions sur le type de code pouvant s'exécuter dans un initialiseur de champ, car vous ne pouvez pas simplement masquer le
this
pointeur.Comment initialiser des tableaux de types de référence? Considérez un
List<T>
qui est soutenu par un tableau avec une capacité supérieure à la longueur. Les éléments restants doivent avoir une certaine valeur.Un autre problème est qu'il n'autorise pas les méthodes telles
bool TryGetValue<T>(key, out T value)
que return,default(T)
commevalue
si elles ne trouvaient rien. Bien que, dans ce cas, il soit facile de soutenir que le paramètre out est une mauvaise conception en premier lieu et que cette méthode devrait renvoyer une union discriminante ou peut - être à la place.Tous ces problèmes peuvent être résolus, mais ce n'est pas aussi simple que "interdire la nullité et tout va bien".
la source
List<T>
meilleur exemple est l’IMHO, car il faudrait soit que chaqueT
élément ait une valeur par défaut, que chaque élément du magasin de stockage secondaire soitMaybe<T>
associé à un champ "isValid" supplémentaire, même lorsqueT
est unMaybe<U>
, ou que le code correspondantList<T>
se comporte différemment selon sur siT
est lui-même un type nullable. Je considérerais que l'initialisation desT[]
éléments sur une valeur par défaut est le moindre mal de ces choix, mais cela signifie bien sûr que les éléments doivent avoir une valeur par défaut.Les langages de programmation les plus utiles permettent d’écrire et de lire des éléments de données en séquences arbitraires, de sorte qu’il sera souvent impossible de déterminer de manière statique l’ordre dans lequel les lectures et les écritures auront lieu avant l’exécution d’un programme. Il existe de nombreux cas où le code stocke effectivement des données utiles dans chaque emplacement avant de le lire, mais il est difficile de le prouver. Ainsi, il sera souvent nécessaire d'exécuter des programmes où il serait au moins théoriquement possible au code d'essayer de lire quelque chose qui n'a pas encore été écrit avec une valeur utile. Qu'il soit légal ou non de le faire, il n'y a pas de moyen général d'empêcher le code de tenter le coup. La seule question est ce qui devrait se passer quand cela se produit.
Différents langages et systèmes adoptent des approches différentes.
Une approche serait de dire que toute tentative de lire quelque chose qui n'a pas été écrit déclenchera une erreur immédiate.
Une deuxième approche consiste à exiger que le code fournisse une valeur dans chaque emplacement avant de pouvoir le lire, même s’il n’y aurait aucun moyen que la valeur stockée soit sémantiquement utile.
Une troisième approche consiste simplement à ignorer le problème et à laisser ce qui arriverait "naturellement" simplement.
Une quatrième approche consiste à dire que chaque type doit avoir une valeur par défaut, et tout emplacement qui n’a pas été écrit avec quoi que ce soit d’autre aura par défaut cette valeur.
L'approche n ° 4 est beaucoup plus sûre que l'approche n ° 3 et est en général moins chère que les approches n ° 1 et n ° 2. Cela laisse alors la question de ce que la valeur par défaut devrait être pour un type de référence. Pour les types de référence immuables, il serait souvent judicieux de définir une instance par défaut et d'indiquer que la valeur par défaut pour toute variable de ce type doit être une référence à cette instance. Pour les types de référence mutables, cependant, cela ne serait pas très utile. Si vous tentez d'utiliser un type de référence mutable avant qu'il ne soit écrit, il n'y a généralement pas de solution sûre, sauf pour intercepter au point de tentative d'utilisation.
Sémantiquement, si l’on a un tableau
customers
de typeCustomer[20]
et que l’on essaieCustomer[4].GiveMoney(23)
sans rien stocker dans l’Customer[4]
exécution, il va falloir exécuter. On pourrait argumenter qu'une tentative de lectureCustomer[4]
devrait intercepter immédiatement plutôt que d'attendre que le code le tenteGiveMoney
, mais il y a suffisamment de cas où il est utile de lire un emplacement, de découvrir qu'il ne contient pas de valeur, puis de l'utiliser. informations, que l'échec de la tentative de lecture serait souvent une nuisance majeure.Certaines langues permettent de spécifier que certaines variables ne doivent jamais contenir null, et toute tentative de stockage d'une valeur null doit déclencher une interruption immédiate. C'est une fonctionnalité utile. En général, cependant, tout langage permettant aux programmeurs de créer des tableaux de références devra soit permettre la possibilité d’éléments de tableau nuls, soit obliger l’initialisation d’éléments de tableau à des données qui ne peuvent pas avoir de sens.
la source
Maybe
/Option
car si vous n'avez pas une valeur pour votre référence avec # 2 de type résoudre le problème, encore , mais aurez un à l'avenir, il vous suffit de stockerNothing
dans unMaybe <Ref type>
?null
correctement / judicieusement?List<T>
aT[]
ou d'unMaybe<T>
? Qu'en est-il du type de support d'unList<Maybe<T>>
?Maybe
logique pourList
carMaybe
détient une seule valeur. Vouliez-vous direMaybe<T>[]
?Nothing
ne peut être assigné qu'à des valeurs de typeMaybe
, ce n'est donc pas tout à fait comme assignernull
.Maybe<T>
etT
sont deux types distincts.