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)
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órclass 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 Point
s, które same w sobie mają X
i Y
wartości, ale niestety Point
S 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ą MiddleName
s, 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 NullReferenceException
s, 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.)
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:
- zmienna jest niezinicjalizowana. To powinno, najlepiej, Nigdy się nie wydarzyć. Zmienna nie powinna istnieć , chyba że została zainicjowana.
- 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.
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ąć.
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)
.
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:
- odniesienie / wskaźnik jest niezaliczalny: problem jest taki sam jak zmienność w ogóle. Po pierwsze, utrudnia to analizę kodu.
- 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.
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.
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.
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 .
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
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.
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.
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