Dette indlæg er alene udtryk for skribentens egen holdning.

Ned med NULL!

2. juni 2008 kl. 11:597
Artiklen er ældre end 30 dage

Det er almindeligt i mange programmeringssprog, at alle pointere og objektreferencer kan være nulreferencer (null pointers). Det har nogle fordele:

  • I hægtede strukturer såsom lister og træer, kan en nulreference bruges som den tomme liste eller det tomme træ.
  • En nulreference kan bruges som fejlværdi: Hvis en funktion ikke kan returnere en "rigtig" værdi, returnerer den en nulreference.

Men det er heller ikke uden problemer:

  • Alle referencer kan potentielt være nulreferencer, så der skal checkes for dette overalt. Det bliver ofte glemt, og det kan tage meget test at finde manglende null-checks. Så man betaler for bekvemmelighed spredte steder i koden med ubekvemmelighed og potentiel usikkerhed alle andre steder.
  • Hvis en nulreference bruges som tom liste o.lign., så bliver man nødt til at angive fejlværdier anderledes, så at bruge nulreferencer som "standardmetoden" til angivelse af fejlværdier er ikke nogen god ide.

Derfor synes jeg, at implicitte nulreferencer i alle referencetyper bør afskaffes. Der er bedre måder at indikere fejl – enten ved undtagelser (exceptions) eller ved at bruge en type, der eksplicit kan være en fejlværdi eller en normal værdi (og når først man har checket for det, kan kan bruge normalværdien i kontekster, hvor der ikke checkes for fejlværdier). C# 2.0 indførte netop den sidstnævnte slags værdier i form af nullable types, der trods navnet ikke er det samme som implicitte nulreferencer i alle referencetyper (som er det, jeg opponerer mod).

Artiklen fortsætter efter annoncen

Så referencetyper skal ikke have nulreferencer (og det skal verificeres af oversætteren), men alle typer skal have mulighed for at blive udstyret med et eksplicit fejlelement, og det skal være forholdsvis nemt at konvertere mellem typer med og uden fejlelementer.

Der findes en variant af C# kaldet Spec#, som bruger denne ide. Spec# har også mange andre gode ideer, såsom kontrakter, der checkes af oversætteren, så man ikke behøver at teste dem bagefter.

Men ideen er ikke ny. F.eks. har Standard ML aldrig haft nulreferencer. Undtagelser har været med fra starten, og enhver type kan udvides til at være en option-type, der enten er værdien NONE eller en almindelig værdi. Option-typer bliver i de fleste SML-oversættere implementeret som enten en nulreference eller en ikke-nulreference til en værdi, så der er ikke mistet noget effektivitet i forhold til de implicitte nulreferencer – tværtimod, for hvor f.eks. JVM og .NET checker for nulreferencer på køretid, hver gang man følger en reference, så kan disse checks undgås i SML, da oversætteren garanterer, at referencen ikke er nul. Desværre kan man ikke udnytte denne garanti, hvis SML oversættes til JVM eller .NET, da designerne af disse virtuelle maskiner ikke havde fantasi nok til at forestille sig, at man statisk (ret nemt) kan garantere, at refererencer aldrig kan være nul.

7 kommentarer.  Hop til debatten
Denne artikel er gratis...

...men det er dyrt at lave god journalistik. Derfor beder vi dig overveje at tegne abonnement på Version2.

Digitaliseringen buldrer derudaf, og it-folkene tegner fremtidens Danmark. Derfor er det vigtigere end nogensinde med et kvalificeret bud på, hvordan it bedst kan være med til at udvikle det danske samfund og erhvervsliv.

Og der har aldrig været mere akut brug for en kritisk vagthund, der råber op, når der tages forkerte it-beslutninger.

Den rolle har Version2 indtaget siden 2006 - og det bliver vi ved med.

Debatten
Log ind eller opret en bruger for at deltage i debatten.
settingsDebatindstillinger
1
2. juni 2008 kl. 13:47

Mere konkret tænte jeg på, at man f.eks i Java kunne dekorere sine referencer med ? eller !, så koden så sadant ud: Object! objNotNull = new Object(); Object? objMayBeNull; // Implicit = null;

Men ellers: Vi kunne jo blot kode i et ordenligt sprog som SML istedet for at blive hængende i videreudvikliger af C..

2
2. juni 2008 kl. 14:59

ptr = NULL; // C null pointer. Skal bare crashe programmet. ptr = Null; // Database Null. Alt hvad den rører ved bliver Null. ptr = NIL; // Lisp nil. Derefererer altid til noget, der er nil. ptr = undef; // Perl undef. Evaluerer til "", 0, () eller noget andet 0-agtigt.

Så kan enhver jo bare bruge det, der falder en mest nært. :-)

Thomas

3
2. juni 2008 kl. 16:18

Hej Torben

Som .net udvikler syntes jeg virkeligt at non-nullable parametre fra Spect# er spændende og jeg kunne rigtigt godt tænke mig at se dem implementeret i C#. Jeg tror nok de er erklæret som f.eks. foo(string! param) hvilket betyder at compileren checker om der er en mulighed for at param er null og giver en compilefejl hvis den kan være. Det er rart fordi man kan flytte ansvaret for null værdier væk fra implementering af metoden og over til kalderen af metoden. Det giver en simplere implementering. Generelt er inspirationen fra "design by contract" rigtig spændende at se implementeret i oversættere.

4
2. juni 2008 kl. 16:39

"Derfor synes jeg, at implicitte nulreferencer i alle referencetyper bør afskaffes."

Det minder uhyggeligt om en sekvens i Terry Pratchetts glimrende bog "Jingo" hvor det tørt noteres at det eneste Klatch nogensinde har bidraget til matematik med var: ingenting.

"Alle referencer kan potentielt være nulreferencer, så der skal checkes for dette overalt."

Det tyder på ualmindeligt dårligt programdesign, hvis det er tilfældet.

Faktisk er der alle mulige tegn på at det er alt for lidt med kun en "magisk" værdi, idet en pointers livsforløb ofte er

Udefineret (NULL) Peger på et og andet (addresse) Ikke længere valid (???)

Snedige folk ved at som regel er alle addresser i den første VM page ugyldige, så typisk har man rådighed over 4096 forskellige værdier af "NULL", men det er desværre ikke portabelt.

Poul-Henning

6
3. juni 2008 kl. 11:13

"Alle referencer kan potentielt være nulreferencer, så der skal checkes for dette overalt."</p>
<p>Det tyder på ualmindeligt dårligt programdesign, hvis det er tilfældet.

Så er både JVM og .NET dårligt design, for de checker begge for nulreferencer ved hver dereference.

Pointen er, at det kræver en analyse af hele programmet (på tværs af moduler) for statisk at sikre, at en parameter ikke kan være en nulreference. De harmonerer dårligt med separat oversættelse og endnu værre med dynamisk klasseloading. Hvis en type ikke kan indeholde nulreferencer, er det en global kontrakt, der kan checkes lokalt.

7
Indsendt af Anonym (ikke efterprøvet) den tir, 06/03/2008 - 11:50

Du nævner selv Spec# som et eksempel på et sprog, hvor man i kontrakten kan angive at en parameter ikke må være en nulreference.

Dette er også muligt i Ada, både som en del af definitionen af en referencetype: type Ref_T is access all T; subtype Not_Null_Ref_T is not null Ref_T;

eller i anynome referencetyper: procedure P (A : not null access T); function F (...) return not null access T;

Der vil aldrig blive lavet null-check på en referencetype, der ikke indeholder værdien null.

Men.... Risikoen ved at benytte referencetyper er selvfølgelig stadig til stede. Aliasing-problemet er et "virkeligt" problem, der dagligt volder besvær.

Du nævner ML som en løsning og der er utroligt mange kvaliteter i dette simple og elegante sprog! De var i ML jeg lærte at programmere, selvom jeg troede at jeg allerede kunne alt det der med at kode. Personligt vil jeg anbefale SPARK, selvom jeg ved at Ada (og dermed til dels SPARK) ikke er populær i den akademiske verden.