Verwijderd schreef op 05 augustus 2002 @ 23:35:
[...]
Nu potverdorie, lees eens wat ik schrijf. In de wiskunde
is een boolean een numerieke waarde, en er bestaat een complete algebra om met die waardes te rekenen: boolse algebra. Doe eens een google op
boolean algebra en lees enkele van de meer dan 106000 hits...
Ik ben het met je eens dat een boolse algebra een 0 en 1 heeft voor een true en false, en dat + en * operatoren worden gebruikt om een or en and te maken. Maar dat wil niet zeggen dat je die types met elkaar kan combineren omdat ze toevallig dezelfde characters gebruiken. True en false zijn vollig andere concepten dan de waarden 0 en 1!
Wat c doet is er een rommeltje van maken waarin bv een rekenkundige plus operator wordt gebruikt ipv een boolse plus operator.
code:
1
2
3
4
5
6
| rekenkundig bools
true true
1 1 //conversie
-(1)=-1 -(1)=0 // negatie
-1+1=0 0+1=1 // +1 (rekenkundig:1 erbij, bools:or true)
false true |
Zoals je ziet gaat het behoorlijk fout als je je rekenkundige operaties loslaat op boolse waarden omdat ze volledig andere types zijn. Dit probleem had voorkomen kunnen worden door een boolean type hiervan te maken (zodat ook de juiste boolse operaties waren gekozen) zoals vele andere talen dit wel hebben gedaan.
Maar ik zal c niet volledig afvallen. C is een bloedsnelle taal omdat programmeurs allerlei shortcuts kunnen nemen. C is daarom ook nog steeds de taal voor driver development en os`en en alle andere dingen die bloedsnel moeten zijn. Je moet gewoon weten wanneer je het gebrek aan typesafety(en toegenomen kans op fouten) op prijs gaat stellen in ruil voor snelheid.
If is dus een control-statement. Statements als x= 3 + 5; gedragen zich als expressies in C. Voor de duidelijkheid, de eigenlijke expressie is "3 + 5", maar het resultaat van die expressie wordt door het statement heen gepropageerd zodat dingen als y= x= 3 + 5; mogelijk zijn. Dat kan alleen als het statement "x= 3 + 5" als een expressie wordt geevalueerd.
Dat hoeft niet, je zou dit gerust kunnen maken als productie regel:
code:
1
2
3
| assignment
= (variable '=')+ expression
; |
Zoals je ziet hoeft zo`n declaratie helemaal geen expressie te zijn.
Maar waarom nu toch? Het is zuiver een (taal)conventie of het contract wel gebroken is. Als de taalconventie is dat een integer naar een boolean gedowncast kan worden, dan is er geen breech-of-contract. Je redeneert dus vanuit jouw favoriete taal, en overal waarin C verschilt van het bondage-gedrag van jouw taal zie jij een fout in het design van C. Dat is natuurlijk niet zo, het zijn gewoon talen die ontworpen zijn met een verschillend doel voor ogen: net als in het werkelijke leven, is de docent voor de klas vaak strenger en conservatiever dan de baas op het werk.
Het gaat niet zozeer om een taal conventie, maar het gaat om het zo veel mogelijk de gebruiker(mezelf ook) helpen en daardoor minder tijd nodig ben om allerlei triviale fouten te moeten oplossen.
Ik ben op dit moment bezig met een taal die nog veel strenger is dan java. Het is jou ook vast wel eens overkomen dat je een nullpointer fout hebt gekregen (in het geval van c eventueel 'gek' gedrag). Dit komt omdat je voor ieder adres een null of een non null waarde mag krijgen. Vaak wordt op een of andere manier die null een super type gemaakt van veel types of hij wordt weggestreept in het typemechanisme.
Het probleem is dat je dus geen onderscheid meer kan maken tussen null en non null waardes wat inhoud dat je dus een null binnen kan krijgen terwijl dit volgens het contract niet mag. Vaak worden er precondities gebruik (in dit geval een null check) om alsnog voor te zorgen dat het contract niet verbroken kan worden.
Maar het zou nog veel handiger zijn om aan te geven in het type of je wel of geen null aankan. Je zou bijvoorbeeld kunnen zeggen dat een object waarde
nooit null
kan zijn (object heeft dus per definitie een non null waarde). Als je dan de volgende methode hebt: add(Persoon p) dan weet je dat p nooit null kan zijn, omdat Persoon nooit null kan zijn.
Als je bv wel een null accepteerd dan zou je ook een union type kunnen maken (union type is een type de zich kan gedragen als type A of als type B ). Als je nu het volgende union type gaat maken: union(Persoon,NULL), dan weet je nu dat je dus een null waarde aankan of een echte persoon. Dan zou je als volgt een methode kunnen maken die wel een null aankan:
add(union(Persoon,NULL) p)
en dit zou je kunnen vereenvoudigen mbv syntactisch suiker tot:
add(Persoon? p).
Dezelfde '?' zou je ook kunnen toepassen in de rest van de taal.
(voor de duidelijkheid NULL is het type van null)
Stel nu dat je de volgende methode hebt:
void add(Persoon p){
...
}
en het volgende stukje aanroepende code:
Persoon? jan = ...;
add(jan)
dan krijg je een compiletime type foutmelding, omdat het type van jan ruimer is dan het type van p (jan is dus null of non null, en de methode kan alleen non null aan).
Dit is een klein voorbeeldje hoe je door strengere typering compile time voor safety kan zorgen. In dit geval zal het dus nooit meer voorkomen dat je een methode aanroept met een null als hij daar niet voor gebouwd is, want je krijgt nu compile time al een foutmelding.
Over het algemeen kun je met een strenger type systeem veel elegantere code in elkaar zetten omdat je code niet meer 'vervuild' raakt met allerlei preconditie code of gammel worden door gebrek aan. Maar het belangrijkste punt aan mijn verhaal is dat code een middel is om een systeem te modeleren, en ik wil zoveel mogelijk ondersteuning om de correctheid van dat model te garanderen zodat ik kan garanderen dat de software ook doet wat het moet doen.