digitalmars.D - Re: Spec#, nullables and more
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Nov 05 2010
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- Adam D. Ruppe <destructionator gmail.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- Adam D. Ruppe <destructionator gmail.com> Nov 05 2010
- Roman Ivanov <isroman.DEL ETE.km.ru> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Rainer Deyke <rainerd eldwood.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Roman Ivanov <isroman.DEL ETE.km.ru> Nov 06 2010
- tls <do notha.ev> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Adam Burton <adz21c gmail.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Gary Whatmore <no spam.sp> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Gary Whatmore <no spam.sp> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Adam Burton <adz21c gmail.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Adam Burton <adz21c gmail.com> Nov 06 2010
- Rainer Deyke <rainerd eldwood.com> Nov 06 2010
- Bruno Medeiros <brunodomedeiros+spam com.gmail> Nov 26 2010
- =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Roman Ivanov <isroman.DEL ETE.km.ru> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Bruno Medeiros <brunodomedeiros+spam com.gmail> Nov 26 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Adam D. Ruppe <destructionator gmail.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- FeepingCreature <default_357-line yahoo.de> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- FeepingCreature <default_357-line yahoo.de> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- FeepingCreature <default_357-line yahoo.de> Nov 07 2010
- =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> Nov 06 2010
- =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> Nov 05 2010
- Adam Burton <adz21c gmail.com> Nov 05 2010
- Rainer Deyke <rainerd eldwood.com> Nov 05 2010
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Rainer Deyke <rainerd eldwood.com> Nov 06 2010
- Rainer Deyke <rainerd eldwood.com> Nov 06 2010
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- Lutger <lutger.blijdestijn gmail.com> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 06 2010
- bearophile <bearophileHUGS lycos.com> Nov 06 2010
- tls <do notha.ev> Nov 06 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- Walter Bright <newshound2 digitalmars.com> Nov 05 2010
- bearophile <bearophileHUGS lycos.com> Nov 05 2010
- "Denis Koroskin" <2korden gmail.com> Nov 05 2010
- Jonathan M Davis <jmdavisProg gmx.com> Nov 05 2010
- Jimmy Cao <jcao219 gmail.com> Nov 05 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 05 2010
- "Denis Koroskin" <2korden gmail.com> Nov 05 2010
- spir <denis.spir gmail.com> Nov 05 2010
- spir <denis.spir gmail.com> Nov 05 2010
- spir <denis.spir gmail.com> Nov 05 2010
- spir <denis.spir gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- retard <re tard.com.invalid> Nov 06 2010
- retard <re tard.com.invalid> Nov 06 2010
- retard <re tard.com.invalid> Nov 06 2010
- retard <re tard.com.invalid> Nov 06 2010
- retard <re tard.com.invalid> Nov 06 2010
- retard <re tard.com.invalid> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Simen kjaeraas" <simen.kjaras gmail.com> Nov 06 2010
- "Denis Koroskin" <2korden gmail.com> Nov 07 2010
- "Denis Koroskin" <2korden gmail.com> Nov 07 2010
- so <so so.do> Nov 07 2010
- Leandro Lucarella <luca llucax.com.ar> Nov 06 2010
Kagamin:It's hard to tell, whether they fix anything. When you cast nullable to non-nullable, you get your runtime exception as usual, if you if out access to nullable (e.g. in delayed method), you get your runtime exception again or rather logical bug.
I think you are missing about half of the ideas tied to this nonnullable discussion. I suggest you to read the linked article about Spec#. Bye, bearophile
Nov 05 2010
bearophile wrote:I think you are missing about half of the ideas tied to this nonnullable discussion. I suggest you to read the linked article about Spec#.
"The current version of the Spec# program verifier does not check for arithmetic overflow." Oh well!
Nov 05 2010
Walter Bright:"The current version of the Spec# program verifier does not check for arithmetic overflow." Oh well!
Spec# comes from C# that I think has arithmetic overflows at runtime. So I think Spec# has runtime overflows. What that note about Spec# says is that Spec# is not yet able to verify statically (as SPARK does) that a function/program doesn't overflow. Well, there is a way to know, you can use that "useless" site: http://www.rise4fun.com/SpecSharp And try this snippet: class Example { int x; void Inc(int y) requires y > 0; ensures old(x) < x; { checked { x += y; } } } It compiles, so this quite probably means that Spec# has arithmetic overflows (because Spec# is a very strict compiler, it's not sloppy. Is surely signals as error the things it doesn't understand). This also shows you why an online way to test programs is useful. Anyway, the topic of this whole tread is about non-nullable types in D, copying from the very good implementation of Spec# (with the little changes I have shown). Walter, instead of poking and teasing me as a ten year old does, why we don't start talking about serious things? Like, for example if we want nonnull references/pointers in D3. If you aren't interested in this feature we can stop wasting my time. It's not a necessary feature, I will keep using D2 even without it. But it may be something nice to have. Bye, bearophile
Nov 05 2010
bearophile wrote:Walter, instead of poking and teasing me as a ten year old does, why we don't start talking about serious things? Like, for example if we want nonnull references/pointers in D3. If you aren't interested in this feature we can stop wasting my time. It's not a necessary feature, I will keep using D2 even without it. But it may be something nice to have.
I've explained my position and rationale on null many, many times here. tl;dr: null serves the same role as the NaN does for floating point types. It means an invalid value (not an uninitialized value). Invalid values are highly useful as bug detectors (usually mistaken for being a bug). I wish there were a NaN value we could use for integers. Without a null, one is reduced to having to craft a default value that throws exceptions when accessed, ending up in the same place. To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
Nov 05 2010
Walter Bright:I've explained my position and rationale on null many, many times here.
You are right. And sorry for losing my temper a little :-) Thank you for your answer. Bye, bearophile
Nov 05 2010
On 11/5/10 4:31 PM, Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
I agree it's a big misunderstanding, in fact a couple of layered ones. Andrei
Nov 05 2010
Denis Koroskin:I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
The nullables/nonnullables topic is not basic stuff, but it's not a too much complex thing. In my bug report I have explained about adding both the ? and suffix to reference/pointer types (? is for the nullables), so Walter knows and understands what you are saying. A temporary experimental fork of DMD, with nullable types + null path analysis as explained in this thread (I have an idea to improve the way Spec# manages arrays of nonnull references, with a kind of loop variant that in many cases avoids testing all items again) may be created to try these ideas experimentally in D (despite I think it's not a feature for D2, it's for D3). This may show how much bad or good that extension is. But it's a lot of work. Bye, bearophile
Nov 05 2010
Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
Consider non-nullable type T: T[] a = new T[4]; ... time goes by ... T[1] = foo; T[3] = bar; ... more time goes by ... bar(T[2]); In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance. What do I use to default initialize it with non-nullable data? And once I do that, should bar(T[2]) be an error? How would I detect the error? In general, for a non-nullable type, how would I mark an instance as not having meaningful data? For example, an int is a non-nullable type. But there's no int value that means "no meaningful value", and this can hide an awful lot of bugs. I'm not sure at all that non-nullable types do more than make easy to find bugs much, much harder to find.
Nov 05 2010
A non-nullable type is basically just: struct NotNull(T) { T _payload; alias _payload this; invariant() { assert(_payload !is null); } } If we could disable the default constructor, this approach might just work. Sure, the checks here would be at runtime, but you'd have them without having to manually write the assert each time. (Though, having to write NotNull! each time isn't that much of a gain over the asserts, but maybe it would help.)
Nov 05 2010
Adam D. Ruppe wrote:A non-nullable type is basically just: struct NotNull(T) { T _payload; alias _payload this; invariant() { assert(_payload !is null); } } If we could disable the default constructor, this approach might just work. Sure, the checks here would be at runtime, but you'd have them without having to manually write the assert each time.
All that does is reinvent the null pointer seg fault. The hardware does this for you for free. A null pointer is what's known as a "white hole", all attempts to access the object are rejected.
Nov 05 2010
All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
The difference is that the hardware tells you when you use it, whereas the assert tells you when you try to save it. Let me give you an example from my real world code. I have an xml dom class that I use in my websites. An element looks like this: class Element { Element[] children; /* // I didn't write this invariant originally, but always should be there! invariant() { foreach(child; children) assert(child !is null); } */ string toString() { string ret = "<blah>"; foreach(child; children) ret ~= child.toString(); return ret ~ "</blah>"; } } Now, let's use it somewhere for a long time. That children list grows and shrinks as I manipulate the document. Then, when I'm all done, I say: writeln(myElement.toString()); And the hardware only now throws its null pointer segfault, all the way at the very end of the program! But that's not where the real problem was. Somewhere, I did children ~= someFunction(); and someFunction returned null for whatever reason (I don't remember the specifics anymore, but it took almost a full day to track down!) I spent most the day tracking this down and the real problem was around line 200 out of the 3500 line program, but the hardware didn't complain until line 3490! It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
Nov 05 2010
On 11/5/2010 8:51 PM, Adam D. Ruppe wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
The difference is that the hardware tells you when you use it, whereas the assert tells you when you try to save it. Let me give you an example from my real world code. I have an xml dom class that I use in my websites. An element looks like this: class Element { Element[] children; /* // I didn't write this invariant originally, but always should be there! invariant() { foreach(child; children) assert(child !is null); } */ string toString() { string ret = "<blah>"; foreach(child; children) ret ~= child.toString(); return ret ~ "</blah>"; } } Now, let's use it somewhere for a long time. That children list grows and shrinks as I manipulate the document. Then, when I'm all done, I say: writeln(myElement.toString()); And the hardware only now throws its null pointer segfault, all the way at the very end of the program! But that's not where the real problem was. Somewhere, I did children ~= someFunction(); and someFunction returned null for whatever reason (I don't remember the specifics anymore, but it took almost a full day to track down!) I spent most the day tracking this down and the real problem was around line 200 out of the 3500 line program, but the hardware didn't complain until line 3490! It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
Yep, that's exactly the kind of stuff that makes me wish for non-nulls. I've been in the same situation many times. And that's not even the worst case scenario. In the worst case scenario the inadvertent null gets propagated to a 3d party library that uses it in some way that doesn't result in program termination, but changes the logic.
Nov 05 2010
Adam D. Ruppe wrote:It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
Couldn't this happen to you with any datum that has an unexpected value in it? Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
Nov 06 2010
On 11/6/2010 02:42, Walter Bright wrote:Adam D. Ruppe wrote:It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
Couldn't this happen to you with any datum that has an unexpected value in it? Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
That's an argument for limited-range data types, not against non-nullable types. -- Rainer Deyke - rainerd eldwood.com
Nov 06 2010
Rainer Deyke wrote:On 11/6/2010 02:42, Walter Bright wrote:Adam D. Ruppe wrote:It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
in it? Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
That's an argument for limited-range data types, not against non-nullable types.
I see it more as an argument for any restricted subset of a data type. It could be that you want just odd numbers in the array, and an even one crept in.
Nov 06 2010
On 11/6/2010 2:14 PM, Walter Bright wrote:Rainer Deyke wrote:On 11/6/2010 02:42, Walter Bright wrote:Adam D. Ruppe wrote:It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
in it? Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
That's an argument for limited-range data types, not against non-nullable types.
I see it more as an argument for any restricted subset of a data type. It could be that you want just odd numbers in the array, and an even one crept in.
Not quite. I don't want to make an argument in favor of general subsets of data types. (IMO it can be made, but that's a topic of a different discussion.) I just want to point out that nulls are a special case, because they allow anyone to bypass (effortlessly, often unknowingly!) the type checks enforced by class system, which is probably the most important part of object-oriented programming. The problem you outlined above can be solved by creating a class that represents an odd number. But without unnulable types the representation will never be precise, because any variables of that type could (in addition to all the odd numbers) also be null.
Nov 06 2010
Roman Ivanov Wrote:On 11/6/2010 2:14 PM, Walter Bright wrote:Rainer Deyke wrote:On 11/6/2010 02:42, Walter Bright wrote:Adam D. Ruppe wrote:It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
in it? Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
That's an argument for limited-range data types, not against non-nullable types.
I see it more as an argument for any restricted subset of a data type. It could be that you want just odd numbers in the array, and an even one crept in.
Not quite. I don't want to make an argument in favor of general subsets of data types. (IMO it can be made, but that's a topic of a different discussion.) I just want to point out that nulls are a special case, because they allow anyone to bypass (effortlessly, often unknowingly!) the type checks enforced by class system, which is probably the most important part of object-oriented programming.
The silent break when nulls is on purpose right? To make coding easy and fun. I not want test null everywhere. School taught Java and remember nonnull always makes stupid tests and exceptions :-(
Nov 06 2010
Walter:Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
This bug doesn't happen in Ada (and Delphi), because you define a ranged integer type like this: subtype Small is Integer range 1 .. 5; To see an example of this, look at this "useless" site where I have put a small Ada program that you may run yourself, with some stdin data: http://ideone.com/shF5j I try to put inside the "inputx" variable the following numbers: 1 2 1 4 17 But at runtime it gives: raised CONSTRAINT_ERROR : prog.adb:9 range check failed Bye, bearophile
Nov 06 2010
bearophile wrote:Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
This bug doesn't happen in Ada (and Delphi), because you define a ranged integer type like this: subtype Small is Integer range 1 .. 5;
Since you're the second to misunderstand me in the same way, I obviously wasn't very clear at all. Allow me to restate: Suppose, for example, you are appending prime numbers to the array, and somehow appended a 25. Many moons later, something crashes because the 25 was not prime.
Nov 06 2010
Walter Bright wrote:bearophile wrote:Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
This bug doesn't happen in Ada (and Delphi), because you define a ranged integer type like this: subtype Small is Integer range 1 .. 5;
Since you're the second to misunderstand me in the same way, I obviously wasn't very clear at all. Allow me to restate: Suppose, for example, you are appending prime numbers to the array, and somehow appended a 25. Many moons later, something crashes because the 25 was not prime.
value where as 25 is the wrong value. Based on that argument the application should fail immediately on accessing the item with 25 (not many moons later) in the same manner it does nulls, but it doesn't because 25 is the wrong value where as null is a lack of value. As with the array allocation example earlier you initialise the array to nulls to represent the lack of value till your application eventually gets values to assign to the array (which may still be wrong values). As shown by my alternative example non-nulls allow you to define that a variable/parameter wants a value and does not work when it receives nothing. However in the case of the array because all the information is not there at the point of creation it is valid for the array items to represent nothing till you have something to put in them.
Nov 06 2010
Adam Burton wrote:I wouldn't consider that as the same thing. null represents the lack of a value where as 25 is the wrong value. Based on that argument the application should fail immediately on accessing the item with 25 (not many moons later) in the same manner it does nulls, but it doesn't because 25 is the wrong value where as null is a lack of value. As with the array allocation example earlier you initialise the array to nulls to represent the lack of value till your application eventually gets values to assign to the array (which may still be wrong values). As shown by my alternative example non-nulls allow you to define that a variable/parameter wants a value and does not work when it receives nothing. However in the case of the array because all the information is not there at the point of creation it is valid for the array items to represent nothing till you have something to put in them.
I am having a real hard time explaining this. It is conceptually *the same thing*, which is having an enforced subset of the values of a type.
Nov 06 2010
Walter Bright Wrote:Adam Burton wrote:I wouldn't consider that as the same thing. null represents the lack of a value where as 25 is the wrong value. Based on that argument the application should fail immediately on accessing the item with 25 (not many moons later) in the same manner it does nulls, but it doesn't because 25 is the wrong value where as null is a lack of value. As with the array allocation example earlier you initialise the array to nulls to represent the lack of value till your application eventually gets values to assign to the array (which may still be wrong values). As shown by my alternative example non-nulls allow you to define that a variable/parameter wants a value and does not work when it receives nothing. However in the case of the array because all the information is not there at the point of creation it is valid for the array items to represent nothing till you have something to put in them.
I am having a real hard time explaining this. It is conceptually *the same thing*, which is having an enforced subset of the values of a type.
I'm seeing it. The other arguments for non-null types also fall short because non-nulls don't solve basic problems like arrays, basic collections in the library (custom fill policy). Has any mainstream language adopted non-null types? No they haven't because the idea is broken.
Nov 06 2010
Gary Whatmore:I'm seeing it. The other arguments for non-null types also fall short because non-nulls don't solve basic problems like arrays, basic collections in the library (custom fill policy).
Arrays and collections are a subset of the nonull problem I am discussing about. I have shown how Spec# solves this problem. D may choose other solutions.Has any mainstream language adopted non-null types?
Ada has them: http://en.wikibooks.org/wiki/Ada_Programming/Types/access#Null_exclusions Most functional languages are based on nonullables. The GNU version of C has a limited form of them: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#index-g_t_0040code_007bnonnull_007d-function-attribute-2458 The problem is faced by Eiffel too: http://docs.eiffel.com/book/papers/void-safety-how-eiffel-removes-null-pointer-dereferencingNo they haven't because the idea is broken.
I don't think so. Bye, bearophile
Nov 06 2010
bearophile Wrote:Gary Whatmore:I'm seeing it. The other arguments for non-null types also fall short because non-nulls don't solve basic problems like arrays, basic collections in the library (custom fill policy).
Arrays and collections are a subset of the nonull problem I am discussing about. I have shown how Spec# solves this problem. D may choose other solutions.Has any mainstream language adopted non-null types?
Ada has them: http://en.wikibooks.org/wiki/Ada_Programming/Types/access#Null_exclusions Most functional languages are based on nonullables. The GNU version of C has a limited form of them: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#index-g_t_0040code_007bnonnull_007d-function-attribute-2458 The problem is faced by Eiffel too: http://docs.eiffel.com/book/papers/void-safety-how-eiffel-removes-null-pointer-dereferencing
Can't force me to read. I've made my choice of using D already. Nothing will change that.No they haven't because the idea is broken.
I don't think so.
Yes they are.
Nov 06 2010
Simen kjaeraas:3 Can only be created with a nonzero length with a runtime check of a nullable-element array/collection, or as a literal containing only non-nullable elements. ... In many cases, rule #3 will allow you to create your collection using nullable elements, and casting it to non-nullable afterwards.
Spec# uses a kind of that rule 3. The main difference is that that final cast is done on the same variable. So you don't need to do: class T {...} T[5] tempArray; // nulls allowed, the same as T?[5] // creates items of tempArray T [5] array = cast( )tempArray; That's not too much bad. But it requires two different variables. Spec# does something more like this, that needs only one variable: class T {...} T [5] arr; // nulls not allowed // creation phase of array, nulls are allowed inside arr // but you can't read arr contents arr.done(); // imaginary syntax // Now arr is not immutable, you may append nonnulls to // it, replace it items with nonnulls, etc. To allow this, the compiler has to see the type of arr as different before and after the call to the "done" function/method/attribute. This is not different from allowing the usage of assert() to change the state of the type of a variable, as done in Spec# (this is very limited form of typestate): void foo(T x) {...} class T {...} T t = something(); // nullable, the same as T? assert(t !is null); // using D syntax foo(t); // no cast to required here because after // the assert the type system knows t is not null Something similar happens in the branch of this if: void foo(T x) {...} class T {...} T t = something(); // nullable, the same as T? if (t !is null) { foo(t); // no cast to required here } Bye, bearophile
Nov 06 2010
Walter Bright wrote:Adam Burton wrote:I wouldn't consider that as the same thing. null represents the lack of a value where as 25 is the wrong value. Based on that argument the application should fail immediately on accessing the item with 25 (not many moons later) in the same manner it does nulls, but it doesn't because 25 is the wrong value where as null is a lack of value. As with the array allocation example earlier you initialise the array to nulls to represent the lack of value till your application eventually gets values to assign to the array (which may still be wrong values). As shown by my alternative example non-nulls allow you to define that a variable/parameter wants a value and does not work when it receives nothing. However in the case of the array because all the information is not there at the point of creation it is valid for the array items to represent nothing till you have something to put in them.
I am having a real hard time explaining this. It is conceptually *the same thing*, which is having an enforced subset of the values of a type.
explain myself correctly. After reading bearophiles post I think he put it in much better terms of null being a common case that is handled in common way. The value 25 requires far more context for the compiler to identify it as a bad value and determine how the developer should handle it. null can still cause logic errors in the same way 25 can (which could lead to the application outputting bad data or crashing) but there is one particular case that always results in the application behaving in an unexpected manner (attempting to use a null reference on a class type) that makes it different and the compiler can help by enforcing checks. Without non-nullables enforcing that check would be tiresome as it is going to happen all over the place, with non-nullables you actually get the oppurtunity to reduce the amount of checks while also helping the developer check when they should be. The non-nullable propsal doesn't eliminate NPE but it allows you to narrow the locations of the error. I know most of my code doesn't use nulls (you might say I avoid them like the plague) but sometimes code calls for the use of nulls, so if I can reduce the amount of code where a null can go then I have much less code to debug.
Nov 06 2010
Adam Burton wrote:I *think* I undertand what you mean, however I also think I failed to explain myself correctly. After reading bearophiles post I think he put it in much better terms of null being a common case that is handled in common way.
I don't see that non-null is such a special case that it would benefit from a special case syntax. For example: NonNull!T p; v.s. nonnull T p; or worse: nonnull T p;The value 25 requires far more context for the compiler to identify it as a bad value and determine how the developer should handle it.
The compiler wouldn't do it. It would be a user supplied filter for a user defined subtype.The non-nullable propsal doesn't eliminate NPE but it allows you to narrow the locations of the error. I know most of my code doesn't use nulls (you might say I avoid them like the plague) but sometimes code calls for the use of nulls, so if I can reduce the amount of code where a null can go then I have much less code to debug.
The issue of null is only a small part of the possible space of subtyping checks one can find desirable in a program.
Nov 06 2010
Walter:I don't see that non-null is such a special case that it would benefit from a special case syntax.
Well, nonnull are a special cases because: - There is a good enough way to solve this problem. This is not true in the general case. - null exceptions are very common bugs, among the most common ones in Java and C#.For example: NonNull!T p; v.s. nonnull T p; or worse: nonnull T p;
The syntax I have proposed is: T p;The compiler wouldn't do it. It would be a user supplied filter for a user defined subtype. ... The issue of null is only a small part of the possible space of subtyping checks one can find desirable in a program.
Are you using enough OOP in your programs? In OOP programs you manage object references all the time, so finding a null where you expect an object is common enough. The idea of "a user supplied filter for a user defined subtype" looks nice, but in practice has problems because: - It faces a general problem, while the most common problem in OOP code is much more restricted (presence of nulls). - It doesn't allow a short light syntax like the and ? suffixes as I have proposed. - And third, and most importantly, the compiler can't perform the second part of enforcement I was talking about. If we limit ourselves to just nulls, you may use many ways to tell the compiler that a specific reference can't be null: If (x == null) { // in this branch you do something } else { // in this branch the compiler assumes x is a nonnull type! } Or you may add an explicit assert: // some code... assert(x != null); // here the compiler seex x as a nonnullable type // more code... Or even a cast, that performs a run-time test: (cast( )x).someMethod(); Where cast( )x is syntax sugar for cast(typepof(x) )x. I don't know if you may use those three ways if you try to generalize the enforcement of a generic predicate attached to a type (there is a name for such types, we aren't inventing anything in this thread). Bye, bearophile
Nov 06 2010
bearophile wrote:I don't see that non-null is such a special case that it would benefit from a special case syntax.
Well, nonnull are a special cases because: - There is a good enough way to solve this problem. This is not true in the general case. - null exceptions are very common bugs, among the most common ones in Java and C#.
Any type having an unexpected value in it is a very common bug. They often go unnoticed, though, because they don't generate a seg fault. The bug is still there, though. So no, I don't agree it is a special case.
Nov 06 2010
Walter:Any type having an unexpected value in it is a very common bug. They often go unnoticed, though, because they don't generate a seg fault. The bug is still there, though. So no, I don't agree it is a special case.
Then to D3, beside nonnull types, we may also add ranged integers (as integer subtypes or subtypes of other ranges). With both those things you have probably covered a good percentage of what you call "unxpected value" bugs. Bye, bearophile
Nov 06 2010
bearophile wrote:Walter:Any type having an unexpected value in it is a very common bug. They often go unnoticed, though, because they don't generate a seg fault. The bug is still there, though. So no, I don't agree it is a special case.
Then to D3, beside nonnull types, we may also add ranged integers (as integer subtypes or subtypes of other ranges). With both those things you have probably covered a good percentage of what you call "unxpected value" bugs.
Adding ranged integer types increases the coverage from 1% to 2% of the cases. (Pulling random numbers out of the ether, but still, I think the point is valid.)
Nov 06 2010
Walter Bright:Adding ranged integer types increases the coverage from 1% to 2% of the cases. (Pulling random numbers out of the ether, but still, I think the point is valid.)
I don't know if that point is valid. SPARK language is able to have a testable low bug count, and it only has nonnulls and ranged integers. But it also uses other unrelated features, like a very precise and defined semantics, design by contract enforced statically (like Spec#), all functions need to list what outer variables are able to see, plus it has few other safety features (like not allowing recursion, restricting pointer usage a lot, and so on). So despite SPARK doesn't seem to need more than those two kinds of value constraints (nonnulls and ranged integers) I don't know how many bugs those two features alone avoid and how many are left to be caught to the other safety features. My hypothesis is that those two often suffice. But indeed I can't be sure. In an ideal world I'd like to add them to D and then use D for few months and see how much less bugs I put in my code (I keep an updated list of all my bugs). Bye, bearophile
Nov 06 2010
bearophile wrote:Walter Bright:Adding ranged integer types increases the coverage from 1% to 2% of the cases. (Pulling random numbers out of the ether, but still, I think the point is valid.)
I don't know if that point is valid. SPARK language is able to have a testable low bug count, and it only has nonnulls and ranged integers. But it also uses other unrelated features, like a very precise and defined semantics, design by contract enforced statically (like Spec#), all functions need to list what outer variables are able to see, plus it has few other safety features (like not allowing recursion, restricting pointer usage a lot, and so on). So despite SPARK doesn't seem to need more than those two kinds of value constraints (nonnulls and ranged integers) I don't know how many bugs those two features alone avoid and how many are left to be caught to the other safety features. My hypothesis is that those two often suffice. But indeed I can't be sure. In an ideal world I'd like to add them to D and then use D for few months and see how much less bugs I put in my code (I keep an updated list of all my bugs).
Very, very few (if any) dmd issues on bugzilla would have been caught by ranged integers or non-null pointers (despite there being several seg fault bugs). The vast majority of the problems were the result of an incomplete understanding of how things should be done, rather than a coding error. (A coding error being things as simple as a typo or forgetting to initialize something, aka "stupid mistakes".) I think that's in no small part due to my programming for a very long time, and I've developed all kinds of strategies for avoiding the "stupid mistake" kinds of coding errors. As Andrei suggested out to me, and I agree, the notions of non-null types, ranged integers, and the like's primary advantage is not in reducing the bug count, but in enhancing the self-documenting nature of the code. It makes the code easier to reason about, and easier to modify.
Nov 06 2010
Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Walter Bright wrote:=20 Very, very few (if any) dmd issues on bugzilla would have been caught b=
ranged integers or non-null pointers (despite there being several seg fault bugs). =20 The vast majority of the problems were the result of an incomplete understanding of how things should be done, rather than a coding error.=
(A coding error being things as simple as a typo or forgetting to initialize something, aka "stupid mistakes".) =20 I think that's in no small part due to my programming for a very long time, and I've developed all kinds of strategies for avoiding the "stupid mistake" kinds of coding errors. =20 As Andrei suggested out to me, and I agree, the notions of non-null types, ranged integers, and the like's primary advantage is not in reducing the bug count, but in enhancing the self-documenting nature of=
the code. It makes the code easier to reason about, and easier to modif=
Do you consider yourself an average programmer? I wouldn't. You say it yourself: you have a lot of experience at avoiding "stupid mistakes" and the same probably goes for Andrei as well. The vast majority of programmers out there are not at that level (in fact, I would venture a guess that the average level of the people who participate in this newsgroup is way above the global average). As someone who has some experience in managing beginner programmers, I'm pretty sure that non-null pointers and ranged integers would reduce the bug count *for the average programmer*. Jerome --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Nov 07 2010
Walter Bright wrote:Adam Burton wrote:I *think* I undertand what you mean, however I also think I failed to explain myself correctly. After reading bearophiles post I think he put it in much better terms of null being a common case that is handled in common way.
I don't see that non-null is such a special case that it would benefit from a special case syntax.
doesn't get non-nulls it is not like I am gonna run away crying but in my opinion it is different to values like 25.For example: NonNull!T p; v.s. nonnull T p; or worse: nonnull T p;
the compiler then that works for me too. It's not the idea of a compiler change I'd personally want, it's the feature. However as far as I can see the best way to implement it is a compiler change.The value 25 requires far more context for the compiler to identify it as a bad value and determine how the developer should handle it.
The compiler wouldn't do it. It would be a user supplied filter for a user defined subtype.
class types. Most of the time I don't want/need it and having it entails more work in one way or another. Sometimes I do want/need it but that's a much smaller case than when I don't so I'd personally like to say when I want it.The non-nullable propsal doesn't eliminate NPE but it allows you to narrow the locations of the error. I know most of my code doesn't use nulls (you might say I avoid them like the plague) but sometimes code calls for the use of nulls, so if I can reduce the amount of code where a null can go then I have much less code to debug.
The issue of null is only a small part of the possible space of subtyping checks one can find desirable in a program.
even better. While we can't I don't see why this common check can't be *almost* eliminated.
Nov 06 2010
On 11/6/2010 15:00, Walter Bright wrote:I don't see that non-null is such a special case that it would benefit from a special case syntax.
I'm less concerned about syntax and more about semantics. I already use nn_ptr<T> (short for non-null pointer) in C++. It works in C++ because types in C++ are not assumed to have a default constructor. -- Rainer Deyke - rainerd eldwood.com
Nov 06 2010
On 06/11/2010 19:57, Walter Bright wrote:Adam Burton wrote:I wouldn't consider that as the same thing. null represents the lack of a value where as 25 is the wrong value. Based on that argument the application should fail immediately on accessing the item with 25 (not many moons later) in the same manner it does nulls, but it doesn't because 25 is the wrong value where as null is a lack of value. As with the array allocation example earlier you initialise the array to nulls to represent the lack of value till your application eventually gets values to assign to the array (which may still be wrong values). As shown by my alternative example non-nulls allow you to define that a variable/parameter wants a value and does not work when it receives nothing. However in the case of the array because all the information is not there at the point of creation it is valid for the array items to represent nothing till you have something to put in them.
I am having a real hard time explaining this. It is conceptually *the same thing*, which is having an enforced subset of the values of a type.
Indeed, it is the same thing: to enforce a subset of the values of a type (and these are contracts, generally speaking). So, Adam, if you specify as a contract that a certain variable/value should have never 25 on it, and one time it is accessed and it does have 25 there, then there is a bug, and you should consider your application to have failed immediately (even if in practice it likely won't, well, not immediately at least). The only difference in these cases is that wanting to assert something to be nonnull (and have the compiler check that) is *much* more common than any particular restriction on numeric values. But it is not a difference in nature, or concept. -- Bruno Medeiros - Software Engineer
Nov 26 2010
Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Walter Bright wrote:bearophile wrote:Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes=
because the 17 was out of range.
This bug doesn't happen in Ada (and Delphi), because you define a ranged integer type like this: subtype Small is Integer range 1 .. 5;
Since you're the second to misunderstand me in the same way, I obviousl=
wasn't very clear at all. Allow me to restate: =20 Suppose, for example, you are appending prime numbers to the array, and=
somehow appended a 25. Many moons later, something crashes because the 25 was not prime.
I thought D was supposed to be a pragmatic language? All this means is that we need a compromise between what would be ideal (being able to represent arbitrary conditions and have them statically enforced at compile time) and what can be realistically achieved. Your argument seems to be: "since there are cases which we won't be able to handle, let's not do anything at all, not even the cases which we could handle". Jerome --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Nov 06 2010
Jérôme M. Berger wrote:I thought D was supposed to be a pragmatic language? All this means is that we need a compromise between what would be ideal (being able to represent arbitrary conditions and have them statically enforced at compile time) and what can be realistically achieved. Your argument seems to be: "since there are cases which we won't be able to handle, let's not do anything at all, not even the cases which we could handle".
The idea is to find a way to solve this in the general case, not the specific case of non-null pointers. Then we've got dynamite in our hands, rather than warmed over toast.
Nov 06 2010
On 11/6/2010 4:02 PM, Walter Bright wrote:Jérôme M. Berger wrote:I thought D was supposed to be a pragmatic language? All this means is that we need a compromise between what would be ideal (being able to represent arbitrary conditions and have them statically enforced at compile time) and what can be realistically achieved. Your argument seems to be: "since there are cases which we won't be able to handle, let's not do anything at all, not even the cases which we could handle".
The idea is to find a way to solve this in the general case, not the specific case of non-null pointers. Then we've got dynamite in our hands, rather than warmed over toast.
But it's not a specific case if you look at it from OOP perspective! The problem of restricting types was solved in OOP. Every time you inherit a class, you make it more specific. This allows you to restrict types all you want, and the compiler enforces your restrictions. For example, you can create a class that represents an even number and accepts only even numbers in its constructor (trowing exception otherwise). You can then extend it and create a class that only accepts every second even number, and so on. Since D is a statically typed language, attempts to assign arbitrary class to a variable intended for Even objects is caught at compile time (instead of waiting until someone references a missing method or attribute at run-time). Null is the only wrench in the gears here.
Nov 06 2010
Roman Ivanov wrote:But it's not a specific case if you look at it from OOP perspective! The problem of restricting types was solved in OOP. Every time you inherit a class, you make it more specific. This allows you to restrict types all you want, and the compiler enforces your restrictions. For example, you can create a class that represents an even number and accepts only even numbers in its constructor (trowing exception otherwise). You can then extend it and create a class that only accepts every second even number, and so on. Since D is a statically typed language, attempts to assign arbitrary class to a variable intended for Even objects is caught at compile time (instead of waiting until someone references a missing method or attribute at run-time). Null is the only wrench in the gears here.
Your insight that this is related to OOP is right on target. It's polymorphism, but a different kind of polymorphism than class OOP tries to solve.
Nov 06 2010
On 06/11/2010 20:02, Walter Bright wrote:Jérôme M. Berger wrote:I thought D was supposed to be a pragmatic language? All this means is that we need a compromise between what would be ideal (being able to represent arbitrary conditions and have them statically enforced at compile time) and what can be realistically achieved. Your argument seems to be: "since there are cases which we won't be able to handle, let's not do anything at all, not even the cases which we could handle".
The idea is to find a way to solve this in the general case, not the specific case of non-null pointers. Then we've got dynamite in our hands, rather than warmed over toast.
Indeed. I agree that the use-case for non-null has some value, and we should try to look for a general solution, instead of adding specific syntax for it. Not only because D seems very close to be able to do this with library code only, but also because solving this in the general way might allow plenty of other interesting type specifications to be defined. For example, with those changes to struct behavior, I wonder how close we could be to directly specify a type such as Java's wildcards (the <? extends Foo> thingy), that would work with arrays or other containers. -- Bruno Medeiros - Software Engineer
Nov 26 2010
Walter:Suppose, for example, you are appending prime numbers to the array, and somehow appended a 25. Many moons later, something crashes because the 25 was not prime.
There are two main ways to avoid this bug: - Put some test in the code, such test may be an explicit test at the point where you add items to the array (and test that this test works in the unittest of this function), or add a precondition (Contract) that tests the array contents are calling time of the function that inserts the data. Generally in D I now use both unittests and DbC. - Wrap your data inside a struct (that contains an alias this) that has a invariant that makes sure your wrapped numbers are prime (there are type systems that allow you to attach a run-time function like isPrime() to the type system, to allow you to define a type that allows only prime numbers).I see it more as an argument for any restricted subset of a data type. It could be that you want just odd numbers in the array, and an even one crept in.
If I understand what you are meaning, you are trying to say that: nonnulls aren't a general solution, because in general what are "correct" and "wrong" items for an array is a generic predicate, like isPrime(), so having a built-in feature to enforce isNotNull() on a type is useless because it's just a special case. In theory this is true. But it's not the best way to design a language. Experience shows that while your items may need to satisfy a generic predicate, there are many many many situations where the predicate you want to enforce is isNotNull(Sometype) (or isIntegerInsideBound(x,low,hi)). If such enforcement is so common in programs (and if you look at Java code you may see several if == null tests), then the wise thing to do is to put in the language handy shortcuts that allow the programmer this specific case. This means adding the ?/ type annotations I have proposed. As you say, in some situations those ?/ type annotations are useless because you must assure your integer numbers inside the array are prime, but practice shows this is a less common case in a Object Priented language, and even if it's not common, you can't define a simple syntax to enforce it. You may create a syntax to attach generic predicates to a subset type, using a kind of invariant, similar to this (this is syntax sugar for a struct with alias this + struct invariant): typedef int PrimeInt invariant() { return isPrime(this); } This may be useful, but it's not as handy and simple as the isNull() enforcemenet managed by ?/ suffixes. If Spec# if you have a nullable type there are several ways to use it as nonnullable. If (x == null) { // in this branch you do something } else { // in this branch the compiler assumes x is a nonnull type! } Or you may add an explicit assert: // some code... assert(x != null); // here the compiler seex x as a nonnullable type // more code... Or even a cast, that performs a run-time test: (cast( )x).someMethod(); Where cast( )x is syntax sugar for cast(typepof(x) )x. Bye, bearophile
Nov 06 2010
Simen kjaeraas:Bounded!(int, 1, 5)[] myArr; myArr ~= 1; // Compile-time error: int is not implicitly castable to Bounded!(int, 1, 5) myArr ~= Bounded!(int, 1, 5)( 1 ); // Works perfectly
I can't say this is a nice syntax :-) (Isn't the implicit conversion + bound test better?) Bye, bearophile
Nov 06 2010
Simen kjaeraas:Not sure. This way is more explicit, and errors will be caught at compile-time.
I see. But if a syntax is ugly and too much heavy, people (rightly) don't use it... (This is why bounded values are good as builtins). Bye, bearophile
Nov 06 2010
Walter said:Couldn't this happen to you with any datum that has an unexpected value in it?
Yes, indeed. And being able to disable struct default constructors would (I believe anyway) let us fix that in the library too, by forcing initialization upon declaration. Right now, we could write a struct that does runtime checks to ensure its payload is always in range, with the exception of one line: Bounded!(10, 100) myInt; // can't do the check In this specific case, the bounded template could change the default init value for its payload, arbitrarily picking some value in range. But you can't do that with a NotNull struct - there is no default value that's usable, except for null, which we're aiming to avoid! If the default constructor were disable-able, these structs could say: Bounded!(10, 100) myInt; // compile error Bounded!(10, 100) myInt = 10; // calls Bounded.this(10), passing the compile time check, and allowing us to do the runtime check - it automatically runs the struct's invariant! The very same thing allows a not null struct - it forces explicit initialization, which gives the invariant a chance to run, thus catching the error instantly at runtime. While compile time checks are probably more ideal, most of this works in the language today, so I think it's a pretty conservative change that enables a few simple library solutions to this whole range of situations.
Nov 06 2010
Simen kjaeraas wrote:Bounded!(int, 1, 5)[] myArr; myArr ~= 1; // Compile-time error: int is not implicitly castable to Bounded!(int, 1, 5) myArr ~= Bounded!(int, 1, 5)( 1 ); // Works perfectly
Yes, that's it.
Nov 06 2010
Simen kjaeraas wrote:I believe you managed to miss the important part, "If we could disable the default constructor".
Yes, I believe you are correct.
Nov 06 2010
Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
Walter, I know you're a Windows programmer but this cannot be the first time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
Nov 06 2010
FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
Walter, I know you're a Windows programmer but this cannot be the first time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
Why would you want to recover from a seg fault? (asserts in D are not recoverable exceptions)
Nov 06 2010
Walter Bright Wrote:FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
Walter, I know you're a Windows programmer but this cannot be the first time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
Why would you want to recover from a seg fault? (asserts in D are not recoverable exceptions)
You cannot do a lot of things from a signal handler. You can't sjlj, which means sjlj exception handling is out (as are, in fact, most methods of exception handling). This means stack traces are out unless you have special handling for segfaults that decodes the stack and prints the error pos. That in turn means you need to have a debugger attached to get stacktraces, which can be annoying especially in long-running programs where the crash is often the first indication you have of a problem. Furthermore, scope guards will not be run. You can't run the GC from a signal handler because it requires pthreads which is not safe for signal handlers. Garbage collected classes will not be freed. Static class destructors cannot be run. Module destructures can not be safely executed. Can I stop?
Nov 06 2010
FeepingCreature wrote:Walter Bright Wrote:FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
(asserts in D are not recoverable exceptions)
You cannot do a lot of things from a signal handler. You can't sjlj, which means sjlj exception handling is out (as are, in fact, most methods of exception handling). This means stack traces are out unless you have special handling for segfaults that decodes the stack and prints the error pos. That in turn means you need to have a debugger attached to get stacktraces, which can be annoying especially in long-running programs where the crash is often the first indication you have of a problem. Furthermore, scope guards will not be run. You can't run the GC from a signal handler because it requires pthreads which is not safe for signal handlers. Garbage collected classes will not be freed. Static class destructors cannot be run. Module destructures can not be safely executed.
Sure. That's why assert errors are not recoverable.
Nov 06 2010
Walter Bright Wrote:FeepingCreature wrote:Walter Bright Wrote:FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
(asserts in D are not recoverable exceptions)
You cannot do a lot of things from a signal handler. You can't sjlj, which means sjlj exception handling is out (as are, in fact, most methods of exception handling). This means stack traces are out unless you have special handling for segfaults that decodes the stack and prints the error pos. That in turn means you need to have a debugger attached to get stacktraces, which can be annoying especially in long-running programs where the crash is often the first indication you have of a problem. Furthermore, scope guards will not be run. You can't run the GC from a signal handler because it requires pthreads which is not safe for signal handlers. Garbage collected classes will not be freed. Static class destructors cannot be run. Module destructures can not be safely executed.
Sure. That's why assert errors are not recoverable.
I don't see how that addresses what I said in the slightest.
Nov 07 2010
Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware doe=
you for free.
Walter, I know you're a Windows programmer but this cannot be the first=
ER LINUX.=20 Access violations are not a cross-platform substitute for exceptions.
I really, really hope that you can't recover from seg faults on Windows (or MacOS for that matter). Segmentation faults should be non recoverable: once the program has started accessing memory that it shouldn't, there is no way to guarantee that the program is in a coherent state. Walter has stated this several times and I agree with him 100% on this. Jerome --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Nov 06 2010
On 11/06/2010 12:41 AM, Walter Bright wrote:Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
Consider non-nullable type T: T[] a = new T[4]; ... time goes by ... T[1] = foo; T[3] = bar; ... more time goes by ... bar(T[2]); In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance. What do I use to default initialize it with non-nullable data? And once I do that, should bar(T[2]) be an error? How would I detect the error? In general, for a non-nullable type, how would I mark an instance as not having meaningful data? For example, an int is a non-nullable type. But there's no int value that means "no meaningful value", and this can hide an awful lot of bugs. I'm not sure at all that non-nullable types do more than make easy to find bugs much, much harder to find.
I tried to find a good analogy but failed, so I'll just say that in the case you presented you would obviously not use a non-nullable type. As, you know, you wanted nulls in the array. We're using signalling nans to get rid of the null-ish thing for floats, as well.
Nov 05 2010
Walter Bright wrote:Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
Consider non-nullable type T: T[] a = new T[4]; ... time goes by ... T[1] = foo; T[3] = bar; ... more time goes by ... bar(T[2]); In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance. What do I use to default initialize it with non-nullable data? And once I do that, should bar(T[2]) be an error? How would I detect the error? In general, for a non-nullable type, how would I mark an instance as not having meaningful data? For example, an int is a non-nullable type. But there's no int value that means "no meaningful value", and this can hide an awful lot of bugs. I'm not sure at all that non-nullable types do more than make easy to find bugs much, much harder to find.
My understanding on what everyone is requesting is below. class T { private string x; public this(string xin) { x = xin; } public void doSomething() { writeln(x); } } //////Currently//////// function doSomething(T myT) in { Assert(myT is not null); // NPE during debugging on a[3] } { myT.doSomething(); // "1", "2", "null", NPE during release } T[] a = new T[4]; a[0] = new T("1"); a[1] = new T("2"); a[2] = new T(null); foreach(i; a) doSomething(i); ////// With NonNullables. Assume toNonNullable() is override to allow nullables to assign to nonNullables //// function doSomething(T myT) // the in is now redundant, function signature says no nulls allowed { myT.doSomething(); } T?[] a = new T?[4]; // Nullable array items a[0] = new T("1"); a[1] = new T("2"); a[3] = new T(null); // Compile error, null assignment to non-nullable foreach(i; a) doSomething(i); // Compile error potential null assignment to non- nullable, please check for null (e.g. if statement) or override foreach(i; a) doSomething(toNonNullable(i)); // "1", "2", NPE (toNonNullable checks for null and NPE if it is null. However we fail here when attempting to send a null into code that does not allow nulls instead of later. In theory the altered doSomething is saying "don't give me meaningless data". Where as the array says "at this point I don't know enough so I'll take nulls in the mean time". However what we have done is shrink the amount of code a null can live in offering more guarantuees about doSomething. Am I going along the right lines with that?
Nov 05 2010
On 11/5/2010 17:41, Walter Bright wrote:In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance.
That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.) -- Rainer Deyke - rainerd eldwood.com
Nov 05 2010
Rainer Deyke Wrote:That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
Regardless what that is, the presence of nonnull reference types in the language doesn't forbid you to use that idiom used by Walter, because on default in D references and pointers will keep being nullable, so the old code keeps working. Bye, bearophile
Nov 05 2010
Rainer Deyke wrote:On 11/5/2010 17:41, Walter Bright wrote:In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance.
That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
An example would be the bucket array for a hash table. It starts out initially empty, and values get added to it. I have a hard time agreeing that such a ubiquitous and useful data structure is a bad idiom.
Nov 06 2010
Walter:An example would be the bucket array for a hash table. It starts out initially empty, and values get added to it. I have a hard time agreeing that such a ubiquitous and useful data structure is a bad idiom.
I agree, there are several situations where nulls are important and very useful. I have never suggested to remove them. Bye, bearophile
Nov 06 2010
On 11/6/2010 02:47, Walter Bright wrote:Rainer Deyke wrote:On 11/5/2010 17:41, Walter Bright wrote:In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance.
That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
An example would be the bucket array for a hash table. It starts out initially empty, and values get added to it. I have a hard time agreeing that such a ubiquitous and useful data structure is a bad idiom.
Empty is a valid value for a hash table, so that's a completely different situation. Obviously the bucket array would not use a non-nullable type, and less obviously the bucket array should be explicitly initialized to nulls at creation time. -- Rainer Deyke - rainerd eldwood.com
Nov 06 2010
On 11/6/2010 01:12, spir wrote:On Fri, 05 Nov 2010 23:13:44 -0600 Rainer Deyke <rainerd eldwood.com> wrote:That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
You may be right as for local variables. But think at elements of structured data. It constantly happens that one needs to define fields that have no meaningful value at startup, maybe even never will on some instances.
It doesn't happen in dynamic languages. It doesn't happen in pure functional languages, since these languages provide no way to alter a data structure after it has been created. In my experience, it happens very rarely in C++. If it happens "constantly" in D, then that's a flaw in the language. -- Rainer Deyke - rainerd eldwood.com
Nov 06 2010
On 11/6/10 12:13 AM, Rainer Deyke wrote:On 11/5/2010 17:41, Walter Bright wrote:In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance.
That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
To find an array that always has initialized data, look no further than std::vector. There is no way to grow an std::vector without filling it with data under user's control. The only place where std::vector assumes a default is the resize function: void vector<T>::resize(size_type newSize, T filler = T()); If that default went away, the user would always be required to provide a filler when growing the vector. Andrei
Nov 06 2010
Andrei Alexandrescu:To find an array that always has initialized data, look no further than std::vector. There is no way to grow an std::vector without filling it with data under user's control. The only place where std::vector assumes a default is the resize function: void vector<T>::resize(size_type newSize, T filler = T()); If that default went away, the user would always be required to provide a filler when growing the vector.
In D the array append is not an efficient operation, and in general it's not handy to limit to just that the ways to create an array of nonnullables. In another post I have shown how Spec# solves this problem (using a statement to divide the array building phase from the phase where you may read the array items). Bye, bearophile
Nov 06 2010
Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Walter Bright wrote:Consider non-nullable type T: =20 T[] a =3D new T[4]; ... time goes by ... T[1] =3D foo; T[3] =3D bar; ... more time goes by ... bar(T[2]); =20 In other words, I create an array that I mean to fill in later, because=
I don't have meaningful data for it in advance. What do I use to defaul=
initialize it with non-nullable data? And once I do that, should bar(T[2]) be an error? How would I detect the error? =20
Consider immutable type immutable T: immutable T] a =3D new immutable T[4]; ... time goes by ... T[1] =3D foo; T[3] =3D bar; In other words I create an array that I mean to fill in later, because I don't have meaningful data for it in advance. How do I do that with immutable types? </sarcasm> Jerome --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Nov 06 2010
Simen kjaeraas:As others have pointed out, this would be impossible for a proper non-nullable type. The only ways to create an array of non-nullable elements would be a literal, concatenation with non-nullable elements, or casting an existing array of nullable elements. You do not have write access to the length of an array of non-nullable elements, so that you cannot increase the length (thus adding null elements). You can decrease the length by slicing. This way, it should be impossible to have elements of our array that are null.
That's right in theory, but in practice if you want more efficiency, some other solution may be invented. See here for something about the Spec# solution: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=121140 In practice the creation of the array is in two phases, in a phase the array can't be read, and its items can't be used, but they are allowed to be null. Then you use something to tell the compiler that the array is committed. Then you are free to use your array of nonnulls. At that moment the array is mutable still, but you may only assign its elements with nonnulls (using a runtime cast or just the right item of nonnull type), or you may append an nonnull item to the end of the array. Bye, bearophile
Nov 06 2010
retard wrote:Sat, 06 Nov 2010 10:20:24 +0100, Simen kjaeraas wrote:Walter Bright <newshound2 digitalmars.com> wrote:Consider non-nullable type T: T[] a = new T[4];
As others have pointed out, this would be impossible for a proper non-nullable type. The only ways to create an array of non-nullable elements would be a literal, concatenation with non-nullable elements, or casting an existing array of nullable elements.
That's bs.. the functional way to doing this is to wrap all elements in a Maybe monad. It makes the "null check" explicit.
Isn't a list of Maybe T a functional way to express the nullable side effect, rather than express non-nullable types? After all, it is typed as Maybe T, not T. There is a code path for nil in the monadic case right, but not for nullable types. Or do I completely miss the point?
Nov 06 2010
retard wrote:In a functional language: start_the_car c = case c of Just car -> start car Nothing -> error "not initialized"
And the null pointer exception is reinvented!
Nov 06 2010
Walter:retard wrote:In a functional language: start_the_car c = case c of Just car -> start car Nothing -> error "not initialized"
And the null pointer exception is reinvented!
That "case" statement (is a pattern matching) forces you to manage the null case everywhere you use a Nothing-able type like that, otherwise your program doesn't compile. In a language like D the compiler doesn't remind you to manage the null case, so you may forget it. And experience shows that indeed in many people around the world forget to test it and important programs stop running. When you are running a program, it's not nice to see it stop in the middle of its work. This request of explicit management of nulls is indeed the second half of the nonnull proposal I have explained in my enhancement request. There are several ways to face this in D, and so far only a person in this thread has touched this second part of the proposal. Bye, bearophile
Nov 06 2010
bearophile Wrote:Walter:retard wrote:In a functional language: start_the_car c = case c of Just car -> start car Nothing -> error "not initialized"
And the null pointer exception is reinvented!
That "case" statement (is a pattern matching) forces you to manage the null case everywhere you use a Nothing-able type like that, otherwise your program doesn't compile. In a language like D the compiler doesn't remind you to manage the null case, so you may forget it. And experience shows that indeed in many people around the world forget to test it and important programs stop running. When you are running a program, it's not nice to see it stop in the middle of its work.
Walter make this point earlier. That it imrpoves development time when real hardware segfault happens. These other nulls causes runtime or compile time unnecessary checks just like testing prime numbers. Thus not there is need for nonnull pointers. It only complicate the way you think algorithms and make programming hard. I never seen segfault in practice. Have you? I download latest version always it works. And cheap in copy markets.This request of explicit management of nulls is indeed the second half of the nonnull proposal I have explained in my enhancement request. There are several ways to face this in D, and so far only a person in this thread has touched this second part of the proposal.
Walter touched? He gives through out analysis and I think its good the way is now.
Nov 06 2010
Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
I've decided that I should stick my neck out and write up a blog post about this. So stay tuned, armed with roses or bricks as apropos.
Nov 05 2010
bearophile wrote:Anyway, the topic of this whole tread is about non-nullable types in D,
I read your first post as arguing that Spec# in general was a language well designed for security - "Spec# looks very well designed and thought out"copying from the very good implementation of Spec# (with the little changes I have shown). Walter, instead of poking and teasing me as a ten year old does, why we don't start talking about serious things?
Given the store you've set by integer overflow detection, sorry, I couldn't resist teasing you about Spec# failing at that.
Nov 05 2010
Walter:I read your first post as arguing that Spec# in general was a language well designed for security - "Spec# looks very well designed and thought out"
From that tutorial is looks well though-out, in general (but it's not complete yet). I suggest to take a look at how Spec# implements design by contract (ignoring the fact it enforces those contracts statically), because this may give good insights about how to improve D DbC. Bye, bearophile
Nov 05 2010
bearophile wrote:From that tutorial is looks well though-out, in general (but it's not complete yet). I suggest to take a look at how Spec# implements design by contract (ignoring the fact it enforces those contracts statically), because this may give good insights about how to improve D DbC.
From what I recall of Eiffel, a considerably older language (1985), Spec#'s DbC design is almost a direct copy.
Nov 05 2010
Walter Bright:From what I recall of Eiffel, a considerably older language (1985), Spec#'s DbC design is almost a direct copy.
I am not expert about Eiffel, but I have read several documents about Spec#, I have written about thirty little/snippets programs in it, and from what I've seen, Spec# implementation of DbC looks more refined, for all the main corner cases of an OOP language (all that stuff about aggregate objects, ownership, mutable/consistent/committed state for objects, peer groups, and all the relative details. I didn't remember all those things in Eiffel. And those things aren't useless) (but also a bit less flexible, because it's all designed to be statically verifiable). Bye, bearophile
Nov 05 2010
On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
Nov 05 2010
On Friday, November 05, 2010 16:41:25 Walter Bright wrote:Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
Consider non-nullable type T: T[] a = new T[4]; ... time goes by ... T[1] = foo; T[3] = bar; ... more time goes by ... bar(T[2]); In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance. What do I use to default initialize it with non-nullable data? And once I do that, should bar(T[2]) be an error? How would I detect the error? In general, for a non-nullable type, how would I mark an instance as not having meaningful data? For example, an int is a non-nullable type. But there's no int value that means "no meaningful value", and this can hide an awful lot of bugs. I'm not sure at all that non-nullable types do more than make easy to find bugs much, much harder to find.
I thought that the point of non-nullable types were that they _always_ had to have meaningful data. I certainly see no point in a type which just uses a value other than null to indicate that it doesn't have meaningful data. That's just another type of null. Now, I think that your example is a perfect example of one of the reasons why you do _not_ want to have to use non-nullable types. There are cases where you _know_ that a variable should never be null, and having that guarantee can be very useful. However, there are plenty of other cases where you _need_ to be able to have a variable be null, even if it isn't null normally. So, while I wouldn't necessarily be opposed to adding support for non-nullable references and/or pointers to D, I would very much be opposed to making _all_ pointers or references be non-nullable. For that matter, I'd be opposed to it even becoming the default. So, I can understand wanting non-nullable pointers and references, and I'm not really opposed to them being supported, but I wouldn't want to have to use them or have to bend over backwards to get properly nullable pointers and references. - Jonathan M Davis
Nov 05 2010
--0015174c34acc6e76e0494553114 Content-Type: text/plain; charset=ISO-8859-1 On Fri, Nov 5, 2010 at 4:31 PM, Denis Koroskin <2korden gmail.com> wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright < newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
I'm tired of pointing out that NO ONE is talking about eliminating null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
I've been thinking of Vala while reading this thread and its ideas. In Vala one puts a question-mark after a type to mark it as nullable, as all types are non-nullable by default. For instance, instance of T? can be of type T or null. The idea is that D could have it, but the other way around. Could there be support for explicitly specifying that an instance of T can only be T, never null? --0015174c34acc6e76e0494553114 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable <div>On Fri, Nov 5, 2010 at 4:31 PM, Denis Koroskin=A0<span dir=3D"ltr"><= ;<a href=3D"mailto:2korden gmail.com">2korden gmail.com</a>></span>=A0wr= ote:<br><blockquote class=3D"gmail_quote" style=3D"margin-top: 0px; margin-= right: 0px; margin-bottom: 0px; margin-left: 0.8ex; border-left-width: 1px;= border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-l= eft: 1ex; "> <div class=3D"im">On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <<a = href=3D"mailto:newshound2 digitalmars.com" target=3D"_blank">newshound2 dig= italmars.com</a>> wrote:<br><blockquote class=3D"gmail_quote" style=3D"m= argin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0.8ex; = border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-= style: solid; padding-left: 1ex; "> <br>To eliminate null pointers is the same as shooting the canary in your c= oal mine because its twitter annoys you.<br></blockquote><br></div>I'm = tired of pointing out that NO ONE is talking about eliminating null pointer= s, but rather extending an existing type system to support non-nulls. Your = hate towards non-nullables comes from misunderstanding of the concept.<br> </blockquote></div><div><br></div><div><br></div><div>I've been thinkin= g of Vala while reading this thread and its ideas.<br><div>In Vala one puts= a question-mark after a type to mark it as nullable, as all types are non-= nullable by default.</div> <div>For instance, instance of T? can be of type T or null.=A0</div><div>Th= e idea is that D could have it, but the other way around.</div><div>Could t= here be support for explicitly specifying that an instance of T can only be= T, never null?</div> </div> --0015174c34acc6e76e0494553114--
Nov 05 2010
Walter Bright <newshound2 digitalmars.com> wrote:Adam D. Ruppe wrote:A non-nullable type is basically just: struct NotNull(T) { T _payload; alias _payload this; invariant() { assert(_payload !is null); } } If we could disable the default constructor, this approach might just work. Sure, the checks here would be at runtime, but you'd have them without having to manually write the assert each time.
All that does is reinvent the null pointer seg fault. The hardware does this for you for free. A null pointer is what's known as a "white hole", all attempts to access the object are rejected.
I believe you managed to miss the important part, "If we could disable the default constructor". The invariant here is not about using the NotNull!T, but for when you assign to it. In other words: struct NotNull( T ) if ( is( T == class ) || isPointer!T ) { private T _payload; alias _payload this; this( T data ) { assert( data !is null ); _payload = data; } disable this( ); disable void opAssign( T ); } NotNull!T notNull( T )( T ptr ) { return NotNull!T( ptr ); } It might not cover all bases, but the idea is it is impossible to create a NotNull!T from a null T, and impossible to assign a null T to one. -- Simen
Nov 05 2010
On Sat, 06 Nov 2010 02:41:25 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Denis Koroskin wrote:On Fri, 05 Nov 2010 23:44:58 +0300, Walter Bright <newshound2 digitalmars.com> wrote:To eliminate null pointers is the same as shooting the canary in your coal mine because its twitter annoys you.
null pointers, but rather extending an existing type system to support non-nulls. Your hate towards non-nullables comes from misunderstanding of the concept.
Consider non-nullable type T: T[] a = new T[4]; ... time goes by ... T[1] = foo; T[3] = bar; ... more time goes by ... bar(T[2]); In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance. What do I use to default initialize it with non-nullable data? And once I do that, should bar(T[2]) be an error? How would I detect the error?
How is T non-nullable? By the time you call bar(T[2]) T[2] is not initialized yet, i.e. null. As such, T[] This is clearly not a use-case for non-nullables. In your use case this array contains un-initialized values for some time. Non-nullables means that there is no such state as unitialized. As such, you use T?[], not T[]. If you need an array of elements that are never null, use the following: T[] a = [new T1(), new T2(), new T3()]; or the following: T?[] nullables = ...; // setup a T[] nonNullables = toNonnull(nullables); // or cast(T[])nullables; similar to the way you create mutable objects and then cast to immutable.In general, for a non-nullable type, how would I mark an instance as not having meaningful data?
Err... what? By definition, a non-nullable is an object that can NEVER hold meaningless data. If T must contain meaningless data, use T?, not T.For example, an int is a non-nullable type. But there's no int value that means "no meaningful value", and this can hide an awful lot of bugs.
Use int?, which is a special type that has an additional "not initialized yet" value. This comes at a cost of a larger size though - there is simply no other way to do it.I'm not sure at all that non-nullable types do more than make easy to find bugs much, much harder to find.
You are simply wrong. You clearly misunderstand the concept.
Nov 05 2010
On Fri, 05 Nov 2010 16:41:25 -0700 Walter Bright <newshound2 digitalmars.com> wrote:Consider non-nullable type T: =20 T[] a =3D new T[4]; ... time goes by ... T[1] =3D foo; T[3] =3D bar; ... more time goes by ... bar(T[2]); =20 In other words, I create an array that I mean to fill in later, because I=
have meaningful data for it in advance. What do I use to default initiali=
with non-nullable data? And once I do that, should bar(T[2]) be an error?=
would I detect the error? =20 In general, for a non-nullable type, how would I mark an instance as not =
meaningful data? =20 For example, an int is a non-nullable type. But there's no int value that=
"no meaningful value", and this can hide an awful lot of bugs. =20 I'm not sure at all that non-nullable types do more than make easy to fin=
much, much harder to find.
Maybe the whole point is rather to catch variables left undefined (with a m= eaningful value) before use, isn't it? I was first astonished by D's .init feature. Thought it's a bad idea, as it= lets pass through unitialised vars; and worse, except for floats & pointer= s (maybe more), with a perfectly valid value for the type. Also, when reading "int i;", there is no way to tell whether the programmer= (including myself some time later) actually intends to initialise I with 0= . So, I always write "int i=3D0;". I understand there must be something in the var's memory cell, and rather f= ill it with zeroes or NaN than let it undefined. This aspect of the problem= may mean that non-nullable (or more generally never undefined?) types cann= ot be a solution for languages that need to keep low-level control and effi= ciency like D. On a language that can tolerate to be higher-level (from the above pov), wh= ere for instance all values can be referenced or even wrapped in a struct, = then possibly there would be a solution. I would try to introduce a bottom = type called eg UNDEF, which single value is thus valid for all types. This = would be the unique .init. All operations would fail when passed UNDEF. (Ex= cept for a func or method expression() intended for programmer feedback.) T= hen, I guess it would be possible to do it so that each time a programmer t= ries to use an undefined value, whatever its type, they get "UndefError: at= tempt to use undefined variable...". Is this the actual point of non-nullables? After all, we use null only to m= ean "currently undefined -- don't use me", don't we? Because there must be = something in memory, and we don't want to let there random bits. If I not t= otally wrong, then I currently rather agree that non-nullables are not the = proper tool for D -- but maybe there is no solution at all. (Maybe I'm completely aside the actual question; but I wanted to ask, so th= at at least I know ;-) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 05 2010
On Sat, 6 Nov 2010 05:01:26 +0100 spir <denis.spir gmail.com> wrote: Just wanted to add 2 notes.In general, for a non-nullable type, how would I mark an instance as no=
meaningful data? =20 For example, an int is a non-nullable type. But there's no int value th=
"no meaningful value", and this can hide an awful lot of bugs. =20 I'm not sure at all that non-nullable types do more than make easy to f=
much, much harder to find.
Maybe the whole point is rather to catch variables left undefined (with a=
=20 I was first astonished by D's .init feature. Thought it's a bad idea, as =
ers (maybe more), with a perfectly valid value for the type.Also, when reading "int i;", there is no way to tell whether the programm=
0. So, I always write "int i=3D0;".=20 I understand there must be something in the var's memory cell, and rather=
em may mean that non-nullable (or more generally never undefined?) types ca= nnot be a solution for languages that need to keep low-level control and ef= ficiency like D. In that sense, floats are a non-nullable type. That is indeed a very partic= uliar case: there is a value we can put there in memory, which is strictly = speaking valid for the type, but invalid for any operation.On a language that can tolerate to be higher-level (from the above pov), =
, then possibly there would be a solution. I would try to introduce a botto= m type called eg UNDEF, which single value is thus valid for all types. Thi= s would be the unique .init. All operations would fail when passed UNDEF. (= Except for a func or method expression() intended for programmer feedback.)= Then, I guess it would be possible to do it so that each time a programmer= tries to use an undefined value, whatever its type, they get "UndefError: = attempt to use undefined variable...".=20 Is this the actual point of non-nullables? After all, we use null only to=
e something in memory, and we don't want to let there random bits. If I not= totally wrong, then I currently rather agree that non-nullables are not th= e proper tool for D -- but maybe there is no solution at all. For ints of all sorts, should we have a kind of NaN? -2.e(N-1) (so that as = a side effect we get back symmetry). But then there is testing overhead for= all arithmetic operations. Or introduce this only optionally, for a non-un= def type variant intended for super-safe software.(Maybe I'm completely aside the actual question; but I wanted to ask, so =
=20 =20 Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 =20 spir.wikidot.com =20
-- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 05 2010
On Fri, 05 Nov 2010 16:41:25 -0700 Walter Bright <newshound2 digitalmars.com> wrote:In general, for a non-nullable type, how would I mark an instance as not =
meaningful data? For example, an int is a non-nullable type. But there's no int value that=
"no meaningful value", and this can hide an awful lot of bugs.
In D, a variable can be declared and not explicitely initialised. The probl= em is then, I guess: what bit pattern in memory should the language use to = mean "I'm invalid, don't use me"? (-128 for signed bytes?). this is for run= time checking. NaN, in my opinion, precisely plays this role for floats. See also (*) Now, what about static checking? Is it at all possible for a compiler to st= atically catch every attempt to use a variable of undefined value (whatever= its type)? Should symbol tables have a flag? Or how else do that? Also, how to check all possible control flows pathes? Could there be lingui= stic means helping in that? I mean, syntactic constraints that would make i= t easy for the compiler (and the human reader as well!) to obviously know w= hether a variable is initialised or not at a given point in code? Esp. at f= unc boundaries (parameters of calls, return values). Denis (*) Dynamic languages do not suffer of this issue the same way because one = cannot introduce a variable without giving it a value; which is good. But t= hen, programmers still need some placeholder meaning "undef", esp for struc= tured type fields. For this, languages provide null/nil/none; this conventi= on is possible because _variables_ are untyped. In this sense, null can pla= y in those languages the role of element of a bottom type meaning undef I e= voked in another post. But a new problem arises due to the fact that those languages let one freel= y play with null and pass it around like any other value (or more generally= assignable element). As a consequence, programmers use it like a meaningfu= l element loaded with app semantics. Thus, in a sense, highjacking it for t= heir own use, and breaking the intended purpose of the feature. I guess nul= l should not be assignable, for instance; but instead the language should p= rovide a way to introduce yet undefined vars, that would automatically be s= et to null by the language. (=3D=3D> more or less the role of NaN in D for = floats.) When programmers need a special element to carry app semantics (eg "no auth= or known" in a book database), they should define it especially -- not be a= ble use null/nil/none instead. I think languages could provide a convenienc= e type for those thingies (I call them "Marks"); the point is similar to so= me uses of enums: elements that have a sense but no value properly speaking. -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 05 2010
On Fri, 05 Nov 2010 23:13:44 -0600 Rainer Deyke <rainerd eldwood.com> wrote:On 11/5/2010 17:41, Walter Bright wrote:In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance.
That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
You may be right as for local variables. But think at elements of structure= d data. It constantly happens that one needs to define fields that have no = meaningful value at startup, maybe even never will on some instances. (Even= more in static languages, indeed.) What placeholder can we feed the struct= with? Isn't this precisely the hole .init is intended to fill? What do you think? Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 06 2010
Walter Bright <newshound2 digitalmars.com> wrote:Consider non-nullable type T: T[] a = new T[4];
As others have pointed out, this would be impossible for a proper non-nullable type. The only ways to create an array of non-nullable elements would be a literal, concatenation with non-nullable elements, or casting an existing array of nullable elements. You do not have write access to the length of an array of non-nullable elements, so that you cannot increase the length (thus adding null elements). You can decrease the length by slicing. This way, it should be impossible to have elements of our array that are null. -- Simen
Nov 06 2010
Walter Bright <newshound2 digitalmars.com> wrote:Adam D. Ruppe wrote:It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent.
Couldn't this happen to you with any datum that has an unexpected value in it?
Yes. And by augmenting the type system (such as with non-nullable pointers or bounded integers), such a mistake could be caught early, likely at compile time, because the bounded type or non-null pointer does not allow assignment from arbitrary pointers or values.Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range.
Bounded!(int, 1, 5)[] myArr; myArr ~= 1; // Compile-time error: int is not implicitly castable to Bounded!(int, 1, 5) myArr ~= Bounded!(int, 1, 5)( 1 ); // Works perfectly -- Simen
Nov 06 2010
bearophile <bearophileHUGS lycos.com> wrote:Simen kjaeraas:Bounded!(int, 1, 5)[] myArr; myArr ~= 1; // Compile-time error: int is not implicitly castable to Bounded!(int, 1, 5) myArr ~= Bounded!(int, 1, 5)( 1 ); // Works perfectly
I can't say this is a nice syntax :-) (Isn't the implicit conversion + bound test better?)
Not sure. This way is more explicit, and errors will be caught at compile-time. -- Simen
Nov 06 2010
bearophile <bearophileHUGS lycos.com> wrote:Simen kjaeraas:Not sure. This way is more explicit, and errors will be caught at compile-time.
I see. But if a syntax is ugly and too much heavy, people (rightly) don't use it... (This is why bounded values are good as builtins).
Of course. Now, aliases help a bit here, turning that into something like: alias Bounded!(int, 1, 5) myInt; myInt[] myArr; myArr ~= myInt( 1 ); I believe using an alias would be a good idea in most such cases, as one's bound to write Bounded!(int,1,6) in a long program, and wonder why it doesn't compile. -- Simen
Nov 06 2010
Simen kjaeraas <simen.kjaras gmail.com> wrote:bearophile <bearophileHUGS lycos.com> wrote:Simen kjaeraas:Not sure. This way is more explicit, and errors will be caught at compile-time.
I see. But if a syntax is ugly and too much heavy, people (rightly) don't use it... (This is why bounded values are good as builtins).
Of course. Now, aliases help a bit here, turning that into something like: alias Bounded!(int, 1, 5) myInt; myInt[] myArr; myArr ~= myInt( 1 ); I believe using an alias would be a good idea in most such cases, as one's bound to write Bounded!(int,1,6) in a long program, and wonder why it doesn't compile.
Worth adding too: myInt(648492) is of course not catchable at compile time, while such would be easy with builtin bounded integers. -- Simen
Nov 06 2010
Sat, 06 Nov 2010 02:48:02 -0600, Rainer Deyke wrote:On 11/6/2010 01:12, spir wrote:On Fri, 05 Nov 2010 23:13:44 -0600 Rainer Deyke <rainerd eldwood.com> wrote:That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
You may be right as for local variables. But think at elements of structured data. It constantly happens that one needs to define fields that have no meaningful value at startup, maybe even never will on some instances.
It doesn't happen in dynamic languages. It doesn't happen in pure functional languages, since these languages provide no way to alter a data structure after it has been created. In my experience, it happens very rarely in C++. If it happens "constantly" in D, then that's a flaw in the language.
Scala doesn't have non-nullable references, but the shallow immutability (val) of variables makes many sloppy initialization sequences impossible. Usually the code ends up being of much higher quality when using vals as much as possible.
Nov 06 2010
Sat, 06 Nov 2010 05:04:59 -0400, bearophile wrote:To see an example of this, look at this "useless" site
I have ambivalent feelings.. you're beginning to find the joys of irony, but losing the joys of D :) It's done awful things to me.
Nov 06 2010
Sat, 06 Nov 2010 10:20:24 +0100, Simen kjaeraas wrote:Walter Bright <newshound2 digitalmars.com> wrote:Consider non-nullable type T: T[] a = new T[4];
As others have pointed out, this would be impossible for a proper non-nullable type. The only ways to create an array of non-nullable elements would be a literal, concatenation with non-nullable elements, or casting an existing array of nullable elements.
That's bs.. the functional way to doing this is to wrap all elements in a Maybe monad. It makes the "null check" explicit.
Nov 06 2010
Sat, 06 Nov 2010 13:18:34 +0100, Lutger wrote:retard wrote:Sat, 06 Nov 2010 10:20:24 +0100, Simen kjaeraas wrote:Walter Bright <newshound2 digitalmars.com> wrote:Consider non-nullable type T: T[] a = new T[4];
As others have pointed out, this would be impossible for a proper non-nullable type. The only ways to create an array of non-nullable elements would be a literal, concatenation with non-nullable elements, or casting an existing array of nullable elements.
That's bs.. the functional way to doing this is to wrap all elements in a Maybe monad. It makes the "null check" explicit.
Isn't a list of Maybe T a functional way to express the nullable side effect, rather than express non-nullable types? After all, it is typed as Maybe T, not T. There is a code path for nil in the monadic case right, but not for nullable types. Or do I completely miss the point?
Right, the type 'T' expresses the basic case where you don't have any null/nil values - the non-nullable case. Everything must be initialized with a proper value (lazy initialization is possible, though). The type 'Maybe T' adds the special null case. In D when a function receives a nullable argument, you must do: class Car { void start() {} } void start_the_car(Car c) { if (c != null) c.start(); else throw new Error("The car wasn't initialized!"); } In a functional language: start_the_car c = case c of Just car -> start car Nothing -> error "not initialized" ---- With non-nullable types: class Car { void start() {} } void start_the_car(Car# c) { c.start(); } In a functional language: start_the_car = start
Nov 06 2010
Sat, 06 Nov 2010 11:24:01 -0700, Walter Bright wrote:retard wrote:In a functional language: start_the_car c = case c of Just car -> start car Nothing -> error "not initialized"
And the null pointer exception is reinvented!
What was the point of my post again? To be an inspiration for stupid remarks?
Nov 06 2010
Sat, 06 Nov 2010 21:52:22 +0100, Jérôme M. Berger wrote:FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
Walter, I know you're a Windows programmer but this cannot be the first time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
I really, really hope that you can't recover from seg faults on Windows (or MacOS for that matter). Segmentation faults should be non recoverable: once the program has started accessing memory that it shouldn't, there is no way to guarantee that the program is in a coherent state. Walter has stated this several times and I agree with him 100% on this.
I also agree with him 100% on this. The problem non-nullable types try to solve is they reduce the number of possible segfaults in the first place. I don't care how hard the program crashes. I just don't want my client to experience that ever. Segfaults are terribly common in C/C++ code. I experience those every week. My initial motivation to study/use D was born from these crashes. I can't believe you're not using every possible known mechanism to prevent them.
Nov 06 2010
Gary Whatmore <no spam.sp> wrote:non-nulls don't solve basic problems like arrays, basic collections in the library (custom fill policy).
As I've posted elsewhere, arrays (and other collections) will have to work slightly differently for non-nullable elements: 1 .length is read-only (or read/decrease only) 2 Length can be increased by concatenation only 3 Can only be created with a nonzero length with a runtime check of a nullable-element array/collection, or as a literal containing only non-nullable elements. And yes, there may be cases where this is not good enough, and you need to have nullable elements. That does not mean non-nullable types are borked by default, you just use nullable elements, and accept that you have to work harder to prevent segfaults. In many cases, rule #3 will allow you to create your collection using nullable elements, and casting it to non-nullable afterwards.Can't force me to read. I've made my choice of using D already. Nothing will change that.
He's not trying to make you change language, but to inform you of how other languages have solved the problem. -- Simen
Nov 06 2010
tls <do notha.ev> wrote:I never seen segfault in practice. Have you?
Have you ever touched a computer? I see segfaults about daily. In my own code, not that often. -- Simen
Nov 06 2010
Walter Bright <newshound2 digitalmars.com> wrote:I don't see that non-null is such a special case that it would benefit from a special case syntax. For example:
There are some things that set non-null apart: 1. Few operations change the value of a pointer/class reference: assignment pointer arithmetic ref passing Of these, the last two can be disallowed in most cases. For e.g. an int, operator overloading adds bunches of more cases, and for structs/ classes where fields factor into the calculation, every single public member function and field would need to be wrapped. Simply put, null pointers are low-hanging fruit. 2. There is a single invalid value This is a simplification, of course, as you could argue that a reference to an A is not a valid reference to a B, and pointers may point to arbitrary areas of memory, where nothing of interest may be found. Again, low-hanging fruit.The value 25 requires far more context for the compiler to identify it as a bad value and determine how the developer should handle it.
The compiler wouldn't do it. It would be a user supplied filter for a user defined subtype.
Definitely a possibility, also for non-nullable. However, for such a system to be foolproof (and that's what we want), we need a way to disable the default constructor for structs. There is also the problem of arrays, as I expounded on in a different post. Without language support, this will be a hole in the system. -- Simen
Nov 06 2010
On Sun, 07 Nov 2010 00:58:57 +0300, Walter Bright <newshound2 digitalmars.com> wrote:FeepingCreature wrote:Walter Bright Wrote:FeepingCreature wrote:Walter Bright Wrote:All that does is reinvent the null pointer seg fault. The hardware does this for you for free.
first time somebody has told you this - YOU CANNOT RECOVER FROM SEG FAULTS UNDER LINUX. Access violations are not a cross-platform substitute for exceptions.
(asserts in D are not recoverable exceptions)
which means sjlj exception handling is out (as are, in fact, most methods of exception handling). This means stack traces are out unless you have special handling for segfaults that decodes the stack and prints the error pos. That in turn means you need to have a debugger attached to get stacktraces, which can be annoying especially in long-running programs where the crash is often the first indication you have of a problem. Furthermore, scope guards will not be run. You can't run the GC from a signal handler because it requires pthreads which is not safe for signal handlers. Garbage collected classes will not be freed. Static class destructors cannot be run. Module destructures can not be safely executed.
Sure. That's why assert errors are not recoverable.
And that's why modern languages are designed to *prevent* segfaults from happening using non-nullable types. D has nothing to prevent them, and that's why everyone here is pushing this concept so hard. D has its power in compile-time facilities. Non-nullables will only make this power stronger.
Nov 07 2010
On Sat, 06 Nov 2010 23:37:09 +0300, Gary Whatmore <no spam.sp> wrote:Walter Bright Wrote:Adam Burton wrote:I wouldn't consider that as the same thing. null represents the lack
value where as 25 is the wrong value. Based on that argument the
should fail immediately on accessing the item with 25 (not many moons
in the same manner it does nulls, but it doesn't because 25 is the
value where as null is a lack of value. As with the array allocation example earlier you initialise the array
nulls to represent the lack of value till your application eventually
values to assign to the array (which may still be wrong values). As
my alternative example non-nulls allow you to define that a variable/parameter wants a value and does not work when it receives
However in the case of the array because all the information is not
the point of creation it is valid for the array items to represent
till you have something to put in them.
I am having a real hard time explaining this. It is conceptually *the same thing*, which is having an enforced subset of the values of a type.
I'm seeing it. The other arguments for non-null types also fall short because non-nulls don't solve basic problems like arrays, basic collections in the library (custom fill policy). Has any mainstream language adopted non-null types? No they haven't because the idea is broken.
That's not because the concept is broken, it's because mainstream languages were developed before it became clear that non-nullables are a must. Although the concept itself is an old one, it's wasn't very popular until recently. C# has them (in form of int vs int?). They tried to apply the same concept to reference types, but found no way to do so without breaking existing code. See also http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
Nov 07 2010
Hah people bringing up the argument "bad syntax", when they got nothing to say! Like they type "Matrix!(double, 5, 5)" every time they use a matrix no you never do that, you just: alias Matrix!(double, 5, 5) mat5; // sweet isn't it? Remember! it is a type! On Sat, 06 Nov 2010 12:27:34 +0200, Simen kjaeraas <simen.kjaras gmail.com> wrote:bearophile <bearophileHUGS lycos.com> wrote:Simen kjaeraas:Not sure. This way is more explicit, and errors will be caught at compile-time.
I see. But if a syntax is ugly and too much heavy, people (rightly) don't use it... (This is why bounded values are good as builtins).
Of course. Now, aliases help a bit here, turning that into something like: alias Bounded!(int, 1, 5) myInt; myInt[] myArr; myArr ~= myInt( 1 ); I believe using an alias would be a good idea in most such cases, as one's bound to write Bounded!(int,1,6) in a long program, and wonder why it doesn't compile.
-- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
Walter Bright, el 6 de noviembre a las 01:47 me escribiste:Rainer Deyke wrote:On 11/5/2010 17:41, Walter Bright wrote:In other words, I create an array that I mean to fill in later, because I don't have meaningful data for it in advance.
That's a faulty idiom. A data structure that exists but contains no valid data is a bug waiting to happen - no, it /is/ a bug, even if it does not yet manifest as incorrect observable behavior. (Or at best, it's an unsafe optimization technique that should be wrapped up in an encapsulating function.)
An example would be the bucket array for a hash table. It starts out initially empty, and values get added to it. I have a hard time agreeing that such a ubiquitous and useful data structure is a bad idiom.
In that example, null is *valid* data. Invalid data is when it has no meaning to your algorithm and in you example null has a very important meaning. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Un camión lleno de amigos, míos. Cada uno dando vueltas, en su cabeza. Mientras yo, sufro la picadura de mi propia abeja.
Nov 06 2010