www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Re: Spec#, nullables and more

reply bearophile <bearophileHUGS lycos.com> writes:
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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
 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
next sibling parent Roman Ivanov <isroman.DEL ETE.km.ru> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Roman Ivanov <isroman.DEL ETE.km.ru> writes:
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
parent tls <do notha.ev> writes:
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
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Adam Burton <adz21c gmail.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Gary Whatmore <no spam.sp> writes:
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
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
 No they haven't because the idea is broken.

I don't think so. Bye, bearophile
Nov 06 2010
parent Gary Whatmore <no spam.sp> writes:
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
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent reply Adam Burton <adz21c gmail.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
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
prev sibling next sibling parent Adam Burton <adz21c gmail.com> writes:
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
prev sibling parent Rainer Deyke <rainerd eldwood.com> writes:
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
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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
prev sibling next sibling parent reply =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Roman Ivanov <isroman.DEL ETE.km.ru> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
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
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply FeepingCreature <default_357-line yahoo.de> writes:
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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply FeepingCreature <default_357-line yahoo.de> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent FeepingCreature <default_357-line yahoo.de> writes:
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
prev sibling parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
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
prev sibling next sibling parent =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
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
prev sibling next sibling parent Adam Burton <adz21c gmail.com> writes:
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
prev sibling next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
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
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling parent Rainer Deyke <rainerd eldwood.com> writes:
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
prev sibling next sibling parent Rainer Deyke <rainerd eldwood.com> writes:
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
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
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
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent tls <do notha.ev> writes:
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
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
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
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent Jimmy Cao <jcao219 gmail.com> writes:
--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">&lt= ;<a href=3D"mailto:2korden gmail.com">2korden gmail.com</a>&gt;</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 &lt;<a = href=3D"mailto:newshound2 digitalmars.com" target=3D"_blank">newshound2 dig= italmars.com</a>&gt; 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&#39;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&#39;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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
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
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
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
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
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
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
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
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent retard <re tard.com.invalid> writes:
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
prev sibling next sibling parent retard <re tard.com.invalid> writes:
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
prev sibling next sibling parent retard <re tard.com.invalid> writes:
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
prev sibling next sibling parent retard <re tard.com.invalid> writes:
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
prev sibling next sibling parent retard <re tard.com.invalid> writes:
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
prev sibling next sibling parent retard <re tard.com.invalid> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
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
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
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
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
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
prev sibling parent so <so so.do> writes:
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
prev sibling parent Leandro Lucarella <luca llucax.com.ar> writes:
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