Najlepsze wyjaśnienie dla języków bez null

Co jakiś czas gdy programiści narzekają na błędy/wyjątki null ktoś pyta co robimy bez null.

Mam podstawowe pojęcie o chłodności typów opcji, ale nie mam wiedzy ani umiejętności językowych, aby najlepiej to wyrazić. Co to jest wielkie Wyjaśnienie następujących rzeczy napisane w sposób przystępny dla przeciętnego programisty, do którego moglibyśmy skierować tę osobę?

  • niechciane posiadanie referencji/wskaźników jest nullable przez default
  • Jak działają typy opcji, w tym strategie ułatwiające sprawdzanie przypadków null, takich jak
    • dopasowanie wzorca i
    • składy monadyczne
  • alternatywne rozwiązanie, takie jak wiadomości eating nil
  • (inne aspekty, które przegapiłem)
Author: M. Gruber, 2010-10-21

11 answers

Myślę, że zwięzłe podsumowanie dlaczego null jest niepożądane jest takie, że bezsensowne Stany nie powinny być reprezentowalne .

Przypuśćmy, że modeluję drzwi. Może być w jednym z trzech stanów: otwarty, zamknięty, ale odblokowany oraz zamknięty i zablokowany. Teraz mogę go modelować na wzór
class Door
    private bool isShut
    private bool isLocked

I jest jasne, jak mapować moje trzy stany w te dwie zmienne logiczne. Ale to pozostawia czwarty, niepożądany stan dostępny: isShut==false && isLocked==true. Ponieważ typy, które wybrałem jako moje reprezentacja Przyznaj się do tego stanu, muszę poświęcić wysiłek umysłowy, aby upewnić się, że Klasa nigdy nie dostanie się do tego stanu (być może poprzez jawne kodowanie niezmiennika). W przeciwieństwie do tego, gdybym używał języka z algebraicznymi typami danych lub sprawdzonymi wyliczeniami, który pozwala mi zdefiniować

type DoorState =
    | Open | ShutAndUnlocked | ShutAndLocked

Wtedy mógłbym zdefiniować

class Door
    private DoorState state
I nie ma już zmartwień. System typów zapewni, że istnieją tylko trzy możliwe Stany dla instancji class Door. Tym właśnie są systemy typu dobry at-jednoznacznie wyklucza całą klasę błędów w czasie kompilacji.

Problem z null polega na tym, że każdy typ odniesienia dostaje ten dodatkowy stan w swojej przestrzeni, który jest zazwyczaj niepożądany. Zmienną string może być dowolna sekwencja znaków, lub może to być Ta szalona dodatkowa wartość null, która nie mapuje do mojej domeny problemu. Obiekt Triangle ma trzy Points, które same w sobie mają X i Y wartości, ale niestety PointS lub Triangle Może być to szalona wartość null, która jest bez znaczenia dla domeny graficznej, w której pracuję. Itd.

Kiedy zamierzasz modelować wartość, która prawdopodobnie nie istnieje, powinieneś zdecydować się na nią jawnie. Jeśli mam zamiar modelować ludzi, że każdy Person mA FirstName i LastName, ale tylko niektórzy ludzie mają MiddleNames, to chciałbym powiedzieć coś w stylu

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

Gdzie string tutaj zakłada się, że jest typem Nie-nullable. Wtedy nie ma podstępnych niezmienników do ustalenia i nie ma nieoczekiwanych NullReferenceExceptions, gdy próbuje obliczyć długość czyjegoś imienia. System typów zapewnia, że każdy kod mający do czynienia z MiddleName odpowiada możliwości, że jest to None, podczas gdy każdy kod mający do czynienia z FirstName może bezpiecznie założyć, że istnieje tam wartość.

Więc na przykład, używając typu powyżej, możemy napisać tę głupią funkcję:

let TotalNumCharsInPersonsName(p:Person) =
    let middleLen = match p.MiddleName with
                    | None -> 0
                    | Some(s) -> s.Length
    p.FirstName.Length + middleLen + p.LastName.Length
Bez obaw. W przeciwieństwie do języka z odwołaniami nullable dla typów takich jak string, wtedy zakładając
class Person
    private string FirstName
    private string MiddleName
    private string LastName

Ty koniec tworzenia rzeczy takich jak

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

Który wybucha, jeśli obiekt przychodzącej osoby nie ma niezmiennika wszystkiego, co nie jest null, lub

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)

A może

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

Zakładając, że p zapewnia, że first/last są, ale middle może być null, a może robisz kontrole, które rzucają różne typy wyjątków, lub kto wie co. Wszystkie te szalone wybory implementacyjne i rzeczy do przemyślenia, bo jest taka głupia wartość, której nie chcesz czy potrzebujesz.

Null zwykle dodaje niepotrzebną złożoność.[39]} złożoność jest wrogiem każdego oprogramowania i powinieneś dążyć do zmniejszenia złożoności, gdy tylko jest to uzasadnione.

(zauważ, że nawet te proste przykłady są bardziej skomplikowane. Nawet jeśli FirstName nie może być null, to {[12] } może reprezentować "" (pusty łańcuch), który prawdopodobnie nie jest również nazwą osoby, którą zamierzamy modelować. Jako takie, nawet w przypadku nie nullable ciągów, nadal może być tak, że "reprezentujemy bezsensowne wartości". Ponownie, możesz wybrać walkę z tym albo za pomocą niezmienników i kodu warunkowego w czasie wykonywania, lub za pomocą systemu typów(np. mieć typ NonEmptyString). Ta ostatnia jest być może nierozważna (typy"dobre" są często "zamknięte" przez zbiór wspólnych operacji i np. NonEmptyString nie jest zamknięte przez .SubString(0,0)), ale pokazuje więcej punktów w przestrzeni projektowej. Pod koniec dnia, w każdym systemie typu, jest pewna złożoność, która będzie bardzo dobra w pozbywanie się i innych złożoności, których z natury rzeczy trudniej się pozbyć. Kluczem do tego tematu jest to, że w prawie każdym systemie typów, zmiana z "domyślnych odwołań nullable "na" domyślnych odwołań non-nullable " jest prawie zawsze prostą zmianą, która sprawia, że system typów jest o wiele lepszy w zwalczaniu złożoności i wykluczaniu pewnych typów błędów i bezsensownych Stanów. Więc to dość szalone, że tak wiele języków powtarza ten błąd ponownie i jeszcze raz.)

 417
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 18:47:39

Fajną rzeczą w typach opcji nie jest to, że są opcjonalne. Jest tak, że wszystkie inne typy nie są .

czasami , musimy być w stanie reprezentować rodzaj stanu "null". Czasami musimy reprezentować opcję "bez wartości", jak również inne możliwe wartości, które może przyjąć zmienna. Więc język, który nie pozwala na to, będzie trochę kaleką.

Ale Często nie potrzebujemy tego, a dopuszczenie takiego stanu "null" prowadzi tylko do niejasności i zamieszanie: za każdym razem, gdy uzyskuję dostęp do zmiennej typu odniesienia w. NET, muszę wziąć pod uwagę, że może to być null .

Często, to nigdy faktycznie nie będzie null, ponieważ programista strukturyzuje kod tak, że nigdy nie może się zdarzyć. Ale kompilator nie może tego zweryfikować i za każdym razem, gdy go widzisz, musisz zadać sobie pytanie " czy to może być null? Czy muszę tu sprawdzać czy nie ma null?"

Idealnie, w wielu przypadkach, gdy null nie ma sensu, to nie powinno być dozwolone .

To trudne do osiągnięcia w. NET, gdzie prawie wszystko może być null. Musisz polegać na autorze kodu, który nazywasz, aby był w 100% zdyscyplinowany i konsekwentny oraz miał jasno udokumentowane, co może, a czego nie może być null, albo musisz mieć paranoję i sprawdzać Wszystko .

Jeśli jednak typy nie są nullable domyślnie, nie musisz sprawdzać, czy są null. Wiesz, że nigdy nie mogą być zerowe, ponieważ kompilator / Typ checker wymusza to za Ciebie.

A potem potrzebujemy tylnych drzwi do rzadkich przypadków, w których czy musimy obsłużyć stan null. Następnie można użyć typu "option". Wtedy dopuszczamy null w przypadkach, w których podjęliśmy świadomą decyzję, że musimy być w stanie reprezentować przypadek "no value", a w każdym innym przypadku wiemy, że wartość Nigdy nie będzie null.

Jak już inni wspominali, na przykład w C# lub Javie null może oznaczać jedną z dwóch rzeczy:

  1. zmienna jest niezinicjalizowana. To powinno, najlepiej, Nigdy się nie wydarzyć. Zmienna nie powinna istnieć , chyba że została zainicjowana.
  2. zmienna zawiera pewne "opcjonalne" dane: musi być w stanie reprezentować przypadek, w którym nie ma danych. Czasami jest to konieczne. Być może próbujesz znaleźć obiekt na liście i nie wiesz z góry, czy tam jest. Następnie musimy być w stanie przedstawić, że " żaden obiekt nie był znalezione".

Drugie znaczenie musi zostać zachowane, ale pierwsze powinno zostać całkowicie wyeliminowane. I nawet drugie znaczenie nie powinno być domyślne. To coś, na co możemy się zdecydować , jeśli i kiedy tego potrzebujemy. Ale kiedy nie potrzebujemy czegoś opcjonalnego, chcemy, aby sprawdzacz typu gwarantował, że nigdy nie będzie null.

 62
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 19:09:31

Wszystkie odpowiedzi do tej pory koncentrują się na tym, dlaczego null jest złą rzeczą i jak to jest przydatne, jeśli język może zagwarantować, że pewne wartości nigdy nie będą null.

Następnie sugerują, że byłoby całkiem zgrabnym pomysłem, jeśli wyegzekwujesz nieujemność dla wszystkich wartości, co można zrobić, jeśli dodasz pojęcie jak Option lub Maybe, aby reprezentować typy, które nie zawsze mają określoną wartość. Takie podejście przyjął Haskell.

It ' s all good rzeczy! Nie wyklucza to jednak użycia typów jawnie nullable / non-null w celu osiągnięcia tego samego efektu. Dlaczego więc opcja jest nadal dobra? W końcu Scala obsługuje wartości nullable (is has to, więc może pracować z bibliotekami Javy), ale obsługuje również Options.

Q. jakie są korzyści poza możliwością całkowitego usunięcia null z języka?

A. Skład

Jeśli zrobisz naiwne tłumaczenie z null-aware kod

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

Do kodu świadomego opcji

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

Nie ma wielkiej różnicy! Ale jest to również straszny sposób korzystania z opcji... To podejście jest znacznie czystsze: {]}

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

Lub nawet:

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

Kiedy zaczniesz zajmować się listą opcji, będzie jeszcze lepiej. Wyobraź sobie, że lista people jest sama w sobie opcjonalna:

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

Jak to działa?

//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)

Odpowiedni kod z NULL checks (a nawet elvis ?: operatorzy) byłby boleśnie długo. Prawdziwą sztuczką jest operacja flatMap, która pozwala na zagnieżdżone zrozumienie opcji i kolekcji w sposób, którego wartości nullable nigdy nie mogą osiągnąć.

 43
Author: Kevin Wright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 14:41:15

Ponieważ wydaje się, że ludzie tego brakuje: null jest niejednoznaczne.

Data urodzenia Alicji to null. Co to znaczy?

Data śmierci Boba to null. Co to znaczy?

"rozsądna" interpretacja może być taka, że Data urodzenia Alicji istnieje, ale nie jest znana, podczas gdy Data śmierci Boba nie istnieje (Bob wciąż żyje). Ale dlaczego doszliśmy do różnych odpowiedzi?


Inny problem: null to przypadek krawędzi.

  • jest null = null?
  • jest nan = nan?
  • czy inf = inf?
  • jest +0 = -0?
  • jest +0/0 = -0/0?

Odpowiedzi to Zwykle odpowiednio "tak", "nie", "tak", "tak", "nie", "tak". Szaleni " matematycy "nazywają NaN" nieważnością " i mówią, że jest równa sobie. SQL traktuje null jako nie równe niczym (więc zachowują się jak Nan). Zastanawia się, co się dzieje, gdy próbujesz zapisać ±∞, ±0 i Nan w tej samej kolumnie bazy danych (są 253 NaNs, z których połowa to "negative").

Co gorsza, bazy danych różnią się tym, jak traktują NULL, a większość z nich nie jest spójna (zobacz Obsługa NULL w SQLite). To straszne.


A teraz obowiązkowa historia:

Niedawno zaprojektowałem (sqlite3) tabelę bazy danych z pięcioma kolumnami a NOT NULL, b, id_a, id_b NOT NULL, timestamp. Ponieważ jest to ogólny schemat zaprojektowany w celu rozwiązania ogólnego problemu dla dość arbitralnych aplikacji, istnieją dwie wyjątkowości ograniczenia:

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

id_a istnieje tylko dla kompatybilności z istniejącym projektem aplikacji (częściowo dlatego, że nie wymyśliłem lepszego rozwiązania) i nie jest używany w nowej aplikacji. Ze względu na sposób działania NULL w SQL, mogę wstawić (1, 2, NULL, 3, t) i (1, 2, NULL, 4, t) i nie naruszać pierwszego ograniczenia unikalności(ponieważ (1, 2, NULL) != (1, 2, NULL)).

Działa to szczególnie ze względu na to, jak NULL działa w ograniczeniu unikalności w większości baz danych (prawdopodobnie dlatego łatwiej jest modelować sytuacje "w świecie rzeczywistym", np. żadne dwie osoby nie mogą mieć tego samego numeru ubezpieczenia społecznego, ale nie wszystkie osoby mają jeden).


FWIW, bez uprzedniego wywołania niezdefiniowanego zachowania, odwołania do C++ nie mogą "wskazywać" na null i nie jest możliwe skonstruowanie klasy z niezainicjalizowanymi zmiennymi Odniesienia (jeśli wyjątek zostanie wyrzucony, konstrukcja zawiedzie).

Uwaga boczna: czasami możesz chcieć wzajemnie wykluczających się wskaźników (tzn. tylko jeden z nich może być inny niż NULL), np. w hipotetycznym systemie iOS type DialogState = NotShown | ShowingActionSheet UIActionSheet | ShowingAlertView UIAlertView | Dismissed. Zamiast, Jestem zmuszony do robienia takich rzeczy jak assert((bool)actionSheet + (bool)alertView == 1).

 38
Author: tc.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-29 12:14:03

Niepożądana możliwość posiadania referencji / wskaźników jest domyślnie nullable.

Nie wydaje mi się, aby to był główny problem z NULL, głównym problemem z Null jest to, że mogą one oznaczać dwie rzeczy:

  1. odniesienie / wskaźnik jest niezaliczalny: problem jest taki sam jak zmienność w ogóle. Po pierwsze, utrudnia to analizę kodu.
  2. zmienna będąca null rzeczywiście coś znaczy: tak jest w przypadku, gdy typy opcji faktycznie sformalizuj.

Języki, które obsługują typy opcji, zazwyczaj również zabraniają lub zniechęcają do używania niezainicjalizowanych zmiennych.

Jak działają typy opcji, w tym strategie ułatwiające sprawdzanie przypadków null, takich jak dopasowywanie wzorców.

Aby były skuteczne, typy opcji muszą być obsługiwane bezpośrednio w języku. W przeciwnym razie potrzeba dużo kodu płyty kotła, aby je symulować. Pattern-matching I type-inference to dwa kluczowe cechy języka ułatwianie pracy z typami opcji. Na przykład:

W 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;")

Jednak w języku takim jak Java bez bezpośredniego wsparcia dla typów opcji, mielibyśmy coś w stylu:

//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());

Alternatywne rozwiązanie, takie jak wiadomości eating nil

Objective-C ' s "message eating nil" jest nie tyle rozwiązaniem, co próbą złagodzenia bólu głowy podczas sprawdzania null. Zasadniczo, zamiast rzucać wyjątek runtime podczas próby wywołania metody na obiekcie null, zamiast tego wyrażenie samo ewaluuje do wartości null. Zawieszając niedowierzanie, to tak, jakby każda metoda instancji zaczynała się od if (this == null) return null;. Ale potem dochodzi do utraty informacji: nie wiadomo, czy metoda zwróciła null, ponieważ jest prawidłową zwracaną wartością, czy też obiekt rzeczywiście ma wartość null. Jest to bardzo podobne do połykania wyjątków i nie czyni żadnego postępu w rozwiązywaniu problemów z null opisanych wcześniej.

 16
Author: Stephen Swensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-14 17:17:50

Assembly przyniósł nam adresy znane również jako nieprzepisane wskaźniki. C mapował je bezpośrednio jako wskaźniki typowane, ale wprowadził null Algola jako unikalną wartość wskaźnika, zgodną ze wszystkimi wskaźnikami typowanymi. Duży problem z null w C polega na tym, że ponieważ każdy wskaźnik może być null, nigdy nie można bezpiecznie używać wskaźnika bez ręcznego sprawdzania.

W językach wyższych, posiadanie null jest niezręczne, ponieważ tak naprawdę przekazuje dwa różne pojęcia:

  • mówienie, że coś jest undefined.
  • mówienie, że coś jest opcjonalne.

Posiadanie niezdefiniowanych zmiennych jest praktycznie bezużyteczne i powoduje nieokreślone zachowanie za każdym razem, gdy występują. Przypuszczam, że wszyscy się zgodzą, że rzeczy nieokreślone należy unikać za wszelką cenę.

Drugi przypadek jest opcjonalny i jest najlepiej podany jawnie, na przykład z typem opcji .


Powiedzmy, że jesteśmy w firmie transportowej i musimy stworzyć aplikacja ułatwiająca tworzenie harmonogramu dla naszych kierowców. Dla każdego kierowcy przechowujemy kilka informacji, takich jak: prawa jazdy, które mają i numer telefonu, pod który można zadzwonić w nagłych przypadkach.

W C możemy mieć:

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;
};

Jak zauważysz, w każdym przetwarzaniu naszej listy sterowników będziemy musieli sprawdzić wskaźniki null. Kompilator Ci nie pomoże, bezpieczeństwo programu zależy od twoich barków.

W OCaml ten sam kod wyglądałby jak to:

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;
}
Powiedzmy teraz, że chcemy wydrukować nazwiska wszystkich kierowców wraz z ich numerami praw jazdy.

W 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 ? */
  }
}

W OCaml to by było:

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

Jak widać w tym trywialnym przykładzie, nie ma nic skomplikowanego w wersji bezpiecznej:

    To terser.
  • otrzymujesz znacznie lepsze gwarancje i w ogóle nie jest wymagane sprawdzenie null.
  • kompilator upewnił się, że prawidłowo radzisz sobie z opcja

Podczas gdy w C, mogłeś po prostu zapomnieć o sprawdzeniu zerowym i bum...

Uwaga: te próbki kodu, gdzie nie skompilowane, ale mam nadzieję, że masz pomysły.

 11
Author: bltxd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-23 09:49:48

Microsoft Research ma ciekawy projekt o nazwie

Spec #

Jest to rozszerzenie C# z typu not-null i pewnym mechanizmem sprawdzającym obiekty przed niestanowiącym null , chociaż IMHO stosowanie zasady design by contract może być bardziej odpowiednie i pomocne w wielu kłopotliwych sytuacjach spowodowanych przez odwołania null.

 5
Author: Jahan Zinedine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-16 21:19:05

Robert Nystrom oferuje ładny artykuł tutaj:

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

Opisujący jego proces myślowy przy dodawaniu wsparcia dla nieobecności i niepowodzenia do języka programowania Sroka .

 3
Author: Corbin March,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 21:37:23

Pochodząc z tła. NET, zawsze myślałem, że null ma punkt, jest przydatny. Dopóki nie dowiedziałem się o strukturach i jak łatwo było z nimi pracować, unikając dużo kodu boilerplate. Tony Hoare przemawiając na QCon London w 2009 roku, przeprosił za wynalezienie odniesienia null . Cytuję:

Nazywam to moim błędem miliardowym. To był wynalazek null erygowana w 1965. W tym czasie projektowałem pierwszy kompleksowy system typu dla odniesienia w zorientowanych obiektowo język (ALGOL W). Moim celem było zadbanie o to, aby wszelkie odniesienia powinny być absolutnie bezpieczne, a sprawdzanie wykonywane automatycznie przez kompilator. Ale nie mogłem oprzeć się pokusie, aby umieścić w null odniesienie, po prostu dlatego, że było tak łatwe do wdrożenia. Doprowadziło to do niezliczone błędy, luki i awarie systemu, które mają prawdopodobnie spowodował miliard dolarów bólu i szkód w ciągu ostatnich czterdziestu lat. W ostatnich latach wiele analizatory programów takie jak prefiks i PREfast w Microsofcie zostały użyte do sprawdzania referencji i dawania OSTRZEŻENIA, JEŚLI istnieje ryzyko, mogą być nie-null. Nowsze języki programowania takie jak Spec# wprowadziły deklaracje dla odniesienia inne niż null. Takie rozwiązanie odrzuciłem w 1965 roku.

Zobacz też to pytanie u programistów

 3
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-12 07:31:25

Zawsze patrzyłem na Null (lub nil) jako Brak wartości.

Czasami tego chcesz, czasami nie. to zależy od domeny, z którą pracujesz. Jeśli brak ma znaczenie: brak drugiego imienia, wtedy aplikacja może działać odpowiednio. Z drugiej strony, jeśli wartość null nie powinna tam być: pierwsze imię to null, wtedy programista otrzymuje przysłowiową rozmowę telefoniczną o 2 w nocy.

Widziałem też przeciążony i zbyt skomplikowany kod sprawdzający dla null. Dla mnie oznacza to jedną z dwóch rzeczy:
a) błąd wyżej w drzewie aplikacji
b) zły/niekompletny projekt

Po pozytywnej stronie-Null jest prawdopodobnie jednym z bardziej przydatnych pojęć do sprawdzania, czy czegoś nie ma, a języki bez pojęcia null wytrzymają nadmiernie skomplikowane rzeczy, gdy nadejdzie czas walidacji danych. W tym przypadku, jeśli nowa zmienna nie jest zainicjalizowana, wspomniane languagues Zwykle ustawia zmienne na pusty łańcuch, 0 lub pusty kolekcja. Jeśli jednak pusty łańcuch lub 0 lub kolekcja pusta są poprawnymi wartościami dla Twojej aplikacji -- wtedy masz problem.

Czasami można to obejść poprzez wymyślenie specjalnych / dziwnych wartości dla pól, które reprezentują stan niezauważony. Ale co się stanie, gdy specjalna wartość zostanie wprowadzona przez użytkownika o dobrych intencjach? I nie wdawajmy się w bałagan, który spowoduje procedury walidacji danych. Gdyby język wspierał pojęcie null, wszystkie obawy znikaj.

 1
Author: Jon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-24 16:36:37

Języki wektorowe mogą czasem ujść na sucho z brakiem null.

Pusty wektor służy w tym przypadku jako typowane null.

 0
Author: Joshua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 20:30:21