www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - null [re: spec#]

reply foobar <foo bar.com> writes:
1. the INVENTOR of the "reference" concept himself admits that this is a flawed
design. 
see:  http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake

2. "null" is an a type-system attribute, hence should be checked at compile
time and would have ZERO affect on run-time performance. 
Same as assigning a string value to an int variable. 

3. the notion of an "undefined" state for a type can be generally implemented
by the OPTION type and D pointers are an ad hoc implementation of this concept.
see: http://en.wikipedia.org/wiki/Option_type
Any type can be wrapped by an OPTION type. trying to do the converse of this is
impractical and is bad design. 

4. As already pointed by others, Walter's array should be:
Option!T[] array; 

5. C programmers use null pointers and other special "canary" values as Walter
described mainly as rcodes. This is a BAD and bug-prone way of handling errors
and is replaced in D by a MUCH better mechanism called "Exceptions". I do NOT
want to check for null or zero or NaN throughout my code as if I'm writing c
code! This is why D uses signaling NaNs for floats. 
This accounts for the majority of the uses of null. For the rest just use the
option type!
Nov 06 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
foobar:

 Any type can be wrapped by an OPTION type. trying to do the converse of this
is impractical and is bad design. 

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile
Nov 06 2010
next sibling parent reply foobar <foo bar.com> writes:
bearophile Wrote:

 foobar:
 
 Any type can be wrapped by an OPTION type. trying to do the converse of this
is impractical and is bad design. 

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile

I haven't discussed syntax at all so to make clear: I'm not suggesting modifying existing pointers/references. I think D should add Non-Null references (maybe pointers too). I MUCH prefer to use a T! or T or whatever syntax to denote non-null types than to disable constructors. Disabling stuff is a BAD design and is bug-prone. Prime example is Java's clone method which throws an exception by default. The language should be additive, i.e. I should be writing what I want to do, NOT listing all the possible things that I DON'T want to do.
Nov 06 2010
next sibling parent foobar <yigal100 gmail.com> writes:
retard Wrote:

 Sat, 06 Nov 2010 07:30:56 -0400, foobar wrote:
 
 Disabling stuff is a BAD design and
 is bug-prone. The language should be additive, i.e. I should be
 writing what I want to do, NOT listing all the possible things that I
 DON'T want to do.

Then why are you here?

You just proved your nickname by quoting me out of context.
Nov 06 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
so:

 Fine having a nun-null type or ranged integer or special float whatever in  
 a language library,
 but asking a new syntax for it? Not really.

There's no way you may implement all the relative semantics in the D language. You and Andrei are wrong. Bye, bearophile
Nov 07 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/7/10 10:52 AM, bearophile wrote:
 so:

 Fine having a nun-null type or ranged integer or special float
 whatever in a language library, but asking a new syntax for it? Not
 really.

There's no way you may implement all the relative semantics in the D language. You and Andrei are wrong. Bye, bearophile

What's a relative semantics? And what was exactly the claim that was wrong? Thanks, Andrei
Nov 07 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:

 And what was exactly the claim that was wrong?

That there is no need of compiler & syntax support to implement "good enough" nonnullable reference types in D.
 What's a relative semantics?

- The limited form of typestate change, as shown in the original Spec# paper I've shown; I have explained this five times in recent posts, if you want I may explain again. - Some good solution to the problem of initializing a collection of nonnullable references, a possible solution is explained in the oiriginal Spec# paper I have shown. This too uses a limited form of typestate. But other solutions may be invented that don't require typestates. - Some good solution to the problem of initializing struct/class fields, as explained in the second paper I have linked, regarding "delayed types". Most of that semantics can't be expressed in D, so you need compiler support (and syntax support, but syntax here is very limited, you just need ? notDelayed and something like a "done" for arrays/collections of nonnull references. Syntax is the simplest part of this design). In the end seeing the amount of holes left in the design of the D module system and immutables, this nonnull design may be too much hard for D, most people here don't even see 1/2 of the problems needed to implement nonnullables well enough. So maybe we are just wasting time here. D newsgroup probably needs threads about possible ways to fix/finish the features D2 already has... Bye, bearophile
Nov 07 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
so:

 You are saying as it is something trivial. :)
 Const/Immutable is one of the most important and complex part of a  
 language.

Sorry, I didn't mean to sound like that. It's indeed hard to design the immutable system of D. And my mind is not good enough yet to design it. Nonetheless there are some important holes left in it. The D module system too is essentially unfinished. To me it is becoming increasingly clear that to design a language you need theories, well rounded lumps of ideas that allow you to implement complex features in a comprehensive way, that don't leave too many large corner cases behind (some corner cases are acceptable in a practical language but too much of them are a sign of a "glued together" language). In the end no language is perfect, but probably many new languages fail not because of misfortune or commercial pressures, but because they are badly designed. Another thing that I have just learnt is that it's quite hard to design a language in community. To design a language you need one, two or three very intelligent persons that interact closely with each other. And probably one of those few persons need to be a "theoretician". Bye, bearophile
Nov 07 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/7/10 4:21 PM, bearophile wrote:
 Andrei Alexandrescu:

 And what was exactly the claim that was wrong?

That there is no need of compiler& syntax support to implement "good enough" nonnullable reference types in D.

The only change needed is constructor flow to make sure types with disable'd constructors are always properly initialized. Array construction can be supported with a library function.
 What's a relative semantics?

- The limited form of typestate change, as shown in the original Spec# paper I've shown; I have explained this five times in recent posts, if you want I may explain again. - Some good solution to the problem of initializing a collection of nonnullable references, a possible solution is explained in the oiriginal Spec# paper I have shown. This too uses a limited form of typestate. But other solutions may be invented that don't require typestates. - Some good solution to the problem of initializing struct/class fields, as explained in the second paper I have linked, regarding "delayed types". Most of that semantics can't be expressed in D, so you need compiler support (and syntax support, but syntax here is very limited, you just need ? notDelayed and something like a "done" for arrays/collections of nonnull references. Syntax is the simplest part of this design).

Such work is not new to Spec# (there are papers that introduce non-nullables to Java, with which I'm very familiar). I think simpler solutions are available by restricting constructors appropriately.
 In the end seeing the amount of holes left in the design of the D module
system and immutables, this nonnull design may be too much hard for D, most
people here don't even see 1/2 of the problems needed to implement nonnullables
well enough. So maybe we are just wasting time here. D newsgroup probably needs
threads about possible ways to fix/finish the features D2 already has...

If there are design holes those need to be known. There is awareness and institutional memory (via bugzilla) about bugs in the implementation. Andrei
Nov 07 2010
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 08 November 2010 01:10:07 Simen kjaeraas wrote:
 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 On 11/7/10 4:21 PM, bearophile wrote:
 Andrei Alexandrescu:
 And what was exactly the claim that was wrong?

That there is no need of compiler& syntax support to implement "good enough" nonnullable reference types in D.

The only change needed is constructor flow to make sure types with disable'd constructors are always properly initialized. Array construction can be supported with a library function.

One more thing: structs with NotNull!T fields will need to have their default constructor disabled automagically.

??? Structs have no default constructor. They have an init property with is used for default initialization. And if you could enforce that a struct be properly initialized rather than default initialized, that would be a big change. Granted, I hate the fact that you can't have default constructors on structs, that you cannot enforce that a struct be initialized instead of default initialized, and that you cannot yet use new in CTFE and therefore have structs whose fields which are references be default initialized to null, but as it stands, you cannot get around the problem. Either init needs to be changed to allow for real default constructors and/or objects need to be legal CTFE - ideally both. But while the second is supposed to happen at some point, there is no sign of it happening any time soon, and there appears to be no sign that the first will ever happen. So, there is not currently a way to have NotNull!T hold anything but null initially when it's a member variable of a struct, and there's no sign of that changing any time soon. It is true, however, that the issue must be resolved for NotNull!T to really work. - Jonathan M Davis
Nov 08 2010
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Jonathan M Davis wrote:
 On Monday, November 08, 2010 05:01:57 Simen kjaeraas wrote:
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 ??? Structs have no default constructor.

struct Foo{ disable this(); } Foo f; should halt compilation at Foo f;, and certain other places where a default constructor would normally be used.

I was not aware of that. I didn't think that you could do that for structs since default constructors are illegal in the first place. It would likely have the negative side effect of making it illegal to put Foo in arrays though, since it wouldn't be possible to fill in the array values with init.

That's right. The idea is to allow the declaration of a default constructor for structs, but only if it is marked as disabled. Then, any use of the struct that would require default initialization is disallowed. That includes static arrays, and arrays allocated via new. A dynamic array could be constructed using a literal or by appending the values one by one. This implies that a struct containing a field that has a disabled default constructor also cannot be default initialized. Of course, you can defeat these protections by using a cast and encapsulating the code that does that. I believe Andrei was thinking of this when he mentioned using a template to initialize a nonnull array.
Nov 08 2010
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Jonathan M Davis wrote:
 How hard would it really be to insert code wherever a struct's init value is 
 used to default construct it either when the program starts up (for globals)
or 
 right after it's declared (for locals)? If you could do that, then I don't see 
 why we couldn't have proper default constructors. All I can assume is that 
 adding that extra code to default construct structs would be painful change to 
 make to dmd, but perhaps there's something else that I'm missing. If that
could 
 be done though, it would be a _huge_ improvement.

There are some severe problems with that approach. One of the largest is the possibility that the default constructor may fail (throw an exception). This has a vast impact on the design of things, and wrecks a lot of generic code that relies on being able to guarantee initialization success.
Nov 08 2010
parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Walter Bright (newshound2 digitalmars.com)'s article
 Jonathan M Davis wrote:
 How hard would it really be to insert code wherever a struct's init value is
 used to default construct it either when the program starts up (for globals) or
 right after it's declared (for locals)? If you could do that, then I don't see
 why we couldn't have proper default constructors. All I can assume is that
 adding that extra code to default construct structs would be painful change to
 make to dmd, but perhaps there's something else that I'm missing. If that could
 be done though, it would be a _huge_ improvement.

possibility that the default constructor may fail (throw an exception). This has a vast impact on the design of things, and wrecks a lot of generic code that relies on being able to guarantee initialization success.

While I'm also skeptical of the idea that simply declaring a variable can cause arbitrarily complex code to be executed, I'll play Devil's Advocate a little: What if we required the default struct constructor to be nothrow? We could also require some level of purity (at least weak pure).
Nov 08 2010
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
dsimcha wrote:
 While I'm also skeptical of the idea that simply declaring a variable can cause
 arbitrarily complex code to be executed, I'll play Devil's Advocate a little:
 What if we required the default struct constructor to be nothrow?  We could
also
 require some level of purity (at least weak pure).

Not a bad idea, but this still leaves people nowhere who need non-trivial construction in order to have a valid object.
Nov 08 2010
parent Walter Bright <newshound2 digitalmars.com> writes:
Jonathan M Davis wrote:
 Well, if it's a default constructer which has to be nothrow and maybe pure or 
 nothing, I'll take the default constructor. Now, if there's a better way to 
 handle this which would result in full-blown, arbitrary default constructors, 
 then that would be better, but something is better than nothing.

I just hate to get into the complex, buggy quagmire C++ has with this.
 Another thought relates to the discussion of  disable on a struct's default 
 constructor to disable init. What if we allowed default constructors but 
 disabled init in the same way that we'd be doing with  disable? So, a struct 
 uses init if it has no default constructor, but if it has one, then you can't 
 use it where init would be required. Would that help fix things? It certainly 
 would make some sense given that when using a default constructor, you don't 
 really want init anyway. It could just be annoying with static arrays and the 
 like (though = void should make it possible to make that work I think, if you 
 really need to).

Actually, you do as init takes care of most of the initializations, and ensures the fields have predictable values before the ctor starts.
Nov 08 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, November 08, 2010 15:23:40 Walter Bright wrote:
 dsimcha wrote:
 While I'm also skeptical of the idea that simply declaring a variable can
 cause arbitrarily complex code to be executed, I'll play Devil's
 Advocate a little: What if we required the default struct constructor to
 be nothrow?  We could also require some level of purity (at least weak
 pure).

Not a bad idea, but this still leaves people nowhere who need non-trivial construction in order to have a valid object.

Well, if it's a default constructer which has to be nothrow and maybe pure or nothing, I'll take the default constructor. Now, if there's a better way to handle this which would result in full-blown, arbitrary default constructors, then that would be better, but something is better than nothing. Another thought relates to the discussion of disable on a struct's default constructor to disable init. What if we allowed default constructors but disabled init in the same way that we'd be doing with disable? So, a struct uses init if it has no default constructor, but if it has one, then you can't use it where init would be required. Would that help fix things? It certainly would make some sense given that when using a default constructor, you don't really want init anyway. It could just be annoying with static arrays and the like (though = void should make it possible to make that work I think, if you really need to). - Jonathan M Davis
Nov 08 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 ??? Structs have no default constructor.

True. Disabling the default constructor here means that struct Foo{ disable this(); } Foo f; should halt compilation at Foo f;, and certain other places where a default constructor would normally be used.
 They have an init property with is used for default initialization.

Indeed. And this is different from a default constructor that you can't touch how?
 And if you could enforce that a struct be properly
 initialized rather than default initialized, that would be a big change.

Indeed. And that is what we are trying to achieve.
 Either init needs to be changed to allow for real default constructors  
 and/or objects need to be legal CTFE - ideally both.

Disabling the default constructor (which ostensibly looks this: this( ) { this = init; }) is a very good step in the right direction. Having proper default constructors would be better, and I can not remember the reason we don't have that.
 So, there is not
 currently a way to have NotNull!T hold anything but null initially when  
 it's a
 member variable of a struct, and there's no sign of that changing any  
 time soon.

Indeed, and this will never change - it can't. However, we are arguing that it could be null before the struct's constructor is called, and flow control (or out contracts, if necessary) enforce that it is not null when the constructor returns. I admit I here somewhat miss C++'s initialization lists: struct Foo { NonNull< Bar > bar; Foo( ) : bar( new Bar() ) { } } -- Simen
Nov 08 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Sat, 06 Nov 2010 07:30:56 -0400, foobar wrote:

 Disabling stuff is a BAD design and
 is bug-prone. The language should be additive, i.e. I should be
 writing what I want to do, NOT listing all the possible things that I
 DON'T want to do.

Then why are you here?
Nov 06 2010
prev sibling next sibling parent reply steveh <steveh57 useshotmai.l> writes:
bearophile Wrote:

 foobar:
 
 Any type can be wrapped by an OPTION type. trying to do the converse of this
is impractical and is bad design. 

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different.

I've known there are hidden tradeoffs when using these 'intelligent' types. Non-null types add terrible runtime checks everywhere. It's simply not acceptable in a systems programming language. Operating system kernel uses very efficient uninitialized arrays for buffers. It's crazy to force this non-null hype down our throats. I fail to see why non-null is needed. Sometimes even immutability seems too difficult. You can write safe code, you can write efficient code, and you can write simple code. But these can't be the same code! Is Spec# used anywhere? Don't think so. No TIOBE? No language shootout entries. We should forget it.
Nov 06 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
steveh:

 I've known there are hidden tradeoffs when using these 'intelligent' types.

Right, every design has some trade-offs.
 Non-null types add terrible runtime checks everywhere.

This is not true. They may "add" some runtime checks, but in practice you normally need to perform those cheeks anyway, manually. In practice I think nonnull types may even reduce a bit the total number of cheeks present in critical paths (because if you have a nonnull, there is no need to test it again and again, once is enough).
 It's simply not acceptable in a systems programming language. Operating system
kernel uses very efficient uninitialized arrays for buffers. It's crazy to
force this non-null hype down our throats.

You are just not following the discussion and the things said. The normal pointers and references will keep being used in the same way and they will have no runtime cheeks (the compiler in safe modules may ask in some way for them to be tested before being used, this is a point not discussed enough yet in this thread).
 I fail to see why non-null is needed.

Huge C programs show that many features of D aren't actually necessary, you are able to program and live without them. But I think in theory nonull reference types may help avoid some bug.
 Sometimes even immutability seems too difficult.

It is :-)
 You can write safe code, you can write efficient code, and you can write
simple code. But these can't be the same code!

I agree that those are three things difficult to have at the same time. But I think D may allow you to pick two as you like, this is the good thing.
 Is Spec# used anywhere?

It's used in some universities to teach some ideas it contains. It has created the Contracts of C#4 and a verified C compiler, so even if Spec# dies today, it has done more than 98% of other languages invented so far.
 We should forget it.

Good ideas should be listened to despite the success of the language that contains them. History of computer languages if *full* of failed languages that have taught ideas to more successful languages. If you ignore the ideas just because they come from a failed language, they you are shutting yourself 95% of the progress and development in the story of language design :-) Bye, bearophile
Nov 06 2010
prev sibling parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

steveh wrote:
 Non-null types add terrible runtime checks everywhere.
=20

There are only two possible situations for a given pointer: 1. You know that it cannot be null. In that case, you use a non-nullable type for that pointer to give the information to the compiler. The compiler can then use this information to optimize code better (for example removing null checks inside template instantiations); 2. You don't know if a pointer may be null. In that case, you must check that pointer anyway before using it, so you use a nullable pointer and you're back in the current situation except that the compiler knows to warn you if you forget to check. So what do you gain? Several things: 1. As a library writer, if your function cannot work with a null input, instead of checking the function argument and erroring if it is null, you just use a non-nullable type which pushes the check in the user code where it can be removed altogether in lots of cases; 2. Generic code (templates and mixins) may be optimized better: when generic code includes a null check, the compiler can remove it if it is applied to a non-nullable type; 3. And overall, you get safer code because if you forget to check a nullable pointer, then the compiler can tell you. Note that adding non-nullable types has *nothing* to do with automatically adding hidden runtime checks. I personally think that adding such checks automatically is a bad idea in a "system" language. The proper behaviour should be for the compiler to give an error message (*) so that the programmer can take a look and choose between using a check or changing to a non-nullable pointer. Jerome (*) I didn't say a warning, because I know Walter's view on them, but it could be a warning instead of an error. --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Nov 06 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
steveh <steveh57 useshotmai.l> wrote:

 I've known there are hidden tradeoffs when using these 'intelligent'  
 types. Non-null types add terrible runtime checks everywhere. It's  
 simply not acceptable in a systems programming language. Operating  
 system kernel uses very efficient uninitialized arrays for buffers. It's  
 crazy to force this non-null hype down our throats.

And here you are sorely mistaken. The point of non-nullable types is the exact opposite. In fact, the only time you need a runtime check is when converting a nullable type to a non-nullable - after that, it's guaranteed not to be null. When in addition there is no way to create a non-nullable variable without initializing it (and possibly checking that the initializing value is not null, if it comes from a nullable source), the result is a great decrease in runtime checks, not an increase. -- Simen
Nov 06 2010
prev sibling next sibling parent so <so so.do> writes:
On Sat, 06 Nov 2010 13:30:56 +0200, foobar <foo bar.com> wrote:

 bearophile Wrote:

 foobar:

 Any type can be wrapped by an OPTION type. trying to do the converse  

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile

I haven't discussed syntax at all so to make clear: I'm not suggesting modifying existing pointers/references. I think D should add Non-Null references (maybe pointers too). I MUCH prefer to use a T! or T or whatever syntax to denote non-null types than to disable constructors. Disabling stuff is a BAD design and is bug-prone. Prime example is Java's clone method which throws an exception by default. The language should be additive, i.e. I should be writing what I want to do, NOT listing all the possible things that I DON'T want to do.

Fine having a nun-null type or ranged integer or special float whatever in a language library, but asking a new syntax for it? Not really. D proved that it is capable of supporting anything in library, if you or Bearophile or anyone else that is after this feature got a library solution, no one would be against it. You all must be out of your minds asking such syntax, Isn't there enough retarded languages have that syntax? Thanks. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
 In the end seeing the amount of holes left in the design of the D module  
 system and immutables, this nonnull design may be too much hard for D,  
 most people here don't even see 1/2 of the problems needed to implement  
 nonnullables well enough. So maybe we are just wasting time here. D  
 newsgroup probably needs threads about possible ways to fix/finish the  
 features D2 already has...

 Bye,
 bearophile

You are saying as it is something trivial. :) Const/Immutable is one of the most important and complex part of a language. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent reply Eric Poggel <dnewsgroup2 yage3d.net> writes:
On 11/6/2010 6:50 AM, bearophile wrote:
 foobar:

 Any type can be wrapped by an OPTION type. trying to do the converse of this
is impractical and is bad design.

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile

I still live in D1 land, so forgive me if I'm out of the loop--but what keeps this from being implemented at the library level as templated type: NotNull!(T) ? If there are limitations, maybe these areas of the language can be improved to get us there?
Nov 07 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Simen kjaeraas:

 Context-sensitive constructor disabling is a theoretical possibility, but
 seems to me to conflict with D's other goals.

It's time to update those goals. Bye, bearophile
Nov 08 2010
parent reply steveh <steveh57 useshotmai.l> writes:
bearophile Wrote:

 Simen kjaeraas:
 
 Context-sensitive constructor disabling is a theoretical possibility, but
 seems to me to conflict with D's other goals.

It's time to update those goals.

I studied the situation further. Now I've decided to leave D. I tried to cope with all overly complex type system quirks, but have had enough of it now. These two months with D truly opened my eyes. It means I won't touch C++ or Java either. My next goal is to use an untyped (less types = better) language which concentrates on cool syntax. Intensive test suites guarantee safety and quality. An extreme version of TDD. I'm building an IDE (Eclipse) extension [in Java :( ] for automatically injecting basic tests to typical programs. This way the tests only slow down the debug build. The production version runs at maximum speed. Another solution is exploratory testing. I test stuff interactively using a REPL. These reports and guidelines can be written down in .doc word documents. I learnt this idea from Paul Graham and his new language.
Nov 08 2010
next sibling parent reply foobar <foo bar.com> writes:
steveh Wrote:

 bearophile Wrote:
 
 Simen kjaeraas:
 
 Context-sensitive constructor disabling is a theoretical possibility, but
 seems to me to conflict with D's other goals.

It's time to update those goals.

I studied the situation further. Now I've decided to leave D. I tried to cope with all overly complex type system quirks, but have had enough of it now. These two months with D truly opened my eyes. It means I won't touch C++ or Java either. My next goal is to use an untyped (less types = better) language which concentrates on cool syntax. Intensive test suites guarantee safety and quality. An extreme version of TDD. I'm building an IDE (Eclipse) extension [in Java :( ] for automatically injecting basic tests to typical programs. This way the tests only slow down the debug build. The production version runs at maximum speed. Another solution is exploratory testing. I test stuff interactively using a REPL. These reports and guidelines can be written down in .doc word documents. I learnt this idea from Paul Graham and his new language.

Your conclusion that less types = better is patently incorrect. While I like Ruby and test driven development that doesn't obviate the need for a strong type system. tests can only prove that whatever invalid inputs you test for are accounted for. Tests CANNOT prove the correctness of your system for all inputs. Frankly, I wouldn't use software you wrote with this attitude for a facebook app let alone any system with a smidgen of reliability requirements.
Nov 08 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
foobar:

 While I like Ruby and test driven development that doesn't obviate the need
for a strong type system. 
 tests can only prove that whatever invalid inputs you test for are accounted
for. Tests CANNOT prove the correctness of your system for all inputs.

In practice on average my small Python programs are less buggy than my small D ones. Anyway, in D I use the static type system, many unittests, design by contract, all together at the same time, and sometimes they aren't enough, I'd like a bit of static analysis too (as done by lints or better by more sophisticated languages), or automatic fuzzy tests :-) Bye, bearophile
Nov 08 2010
parent Walter Bright <newshound2 digitalmars.com> writes:
bearophile wrote:
 In practice on average my small Python programs are less buggy than my small D
ones.

One's experience with a particular language can have a very strong influence on how buggy one's code is. D's type system is also designed for large systems. For small programs, static typing is largely irrelevant.
Nov 08 2010
prev sibling next sibling parent dennis luehring <dl.soluz gmx.net> writes:
Intensive test suites guarantee safety and quality. An extreme version of TDD.

sounds good, but strong typesystem pissed poeple normaly don't tend to write such suit code - they just talk about writing it and btw: TDD means Test Driven Development, but in real life it seems to be Trial Driven Development
Nov 08 2010
prev sibling next sibling parent "Manfred_Nowak" <svv1999 hotmail.com> writes:
steveh wrote:

 less types = better

Then no types are best? Do have some fun with assembler. -manfred
Nov 08 2010
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
On 08/11/2010 14:35, steveh wrote:
 bearophile Wrote:

 Simen kjaeraas:

 Context-sensitive constructor disabling is a theoretical possibility, but
 seems to me to conflict with D's other goals.

It's time to update those goals.

I studied the situation further. Now I've decided to leave D. I tried to cope with all overly complex type system quirks, but have had enough of it now. These two months with D truly opened my eyes. It means I won't touch C++ or Java either. My next goal is to use an untyped (less types = better) language which concentrates on cool syntax. Intensive test suites guarantee safety and quality. An extreme version of TDD.

I'm often hearing that argument from the new dynamic languages crowd: that less types = better, and that lots of tests guarantee safety and quality. * lifts hands * What they don't realize, is that static typing is just compile-time unit-testing. :) * hands the spoon back to Neo *
 Another solution is exploratory testing. I test stuff interactively using a
REPL. These reports and guidelines can be written down in .doc word documents.
I learnt this idea from Paul Graham and his new language.

Paul Graham. lol. -- Bruno Medeiros - Software Engineer
Nov 25 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Eric Poggel <dnewsgroup2 yage3d.net> wrote:

 On 11/6/2010 6:50 AM, bearophile wrote:
 foobar:

 Any type can be wrapped by an OPTION type. trying to do the converse  
 of this is impractical and is bad design.

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile

I still live in D1 land, so forgive me if I'm out of the loop--but what keeps this from being implemented at the library level as templated type: NotNull!(T) ? If there are limitations, maybe these areas of the language can be improved to get us there?

NotNull!T needs to have its default constructor disabled. That is the #1 blocker. There are other problems, mostly related to this - classes and structs with NotNull!T fields must define a constructor, and for structs that means the default destructor either needs to be possible to define or possible to disable. Arrays of NotNull!T may not be resized to a greater length, as that would require calling the default constructor of NotNull!T. I think that's it. -- Simen
Nov 08 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:

 On 11/7/10 4:21 PM, bearophile wrote:
 Andrei Alexandrescu:

 And what was exactly the claim that was wrong?

That there is no need of compiler& syntax support to implement "good enough" nonnullable reference types in D.

The only change needed is constructor flow to make sure types with disable'd constructors are always properly initialized. Array construction can be supported with a library function.

One more thing: structs with NotNull!T fields will need to have their default constructor disabled automagically. -- Simen
Nov 08 2010
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 08 Nov 2010 12:08:01 +0300, Simen kjaeraas  
<simen.kjaras gmail.com> wrote:

 Eric Poggel <dnewsgroup2 yage3d.net> wrote:

 On 11/6/2010 6:50 AM, bearophile wrote:
 foobar:

 Any type can be wrapped by an OPTION type. trying to do the converse  
 of this is impractical and is bad design.

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile

I still live in D1 land, so forgive me if I'm out of the loop--but what keeps this from being implemented at the library level as templated type: NotNull!(T) ? If there are limitations, maybe these areas of the language can be improved to get us there?

NotNull!T needs to have its default constructor disabled. That is the #1 blocker. There are other problems, mostly related to this - classes and structs with NotNull!T fields must define a constructor, and for structs that means the default destructor either needs to be possible to define or possible to disable. Arrays of NotNull!T may not be resized to a greater length, as that would require calling the default constructor of NotNull!T. I think that's it.

Not exactly disabled. The following should work: class Foo { this() { bar = new Object(); } NonNull!(Object) bar; // fine here } while the following shouldn't: NonNull!(Object) bar; // error here bar = new Object();
Nov 08 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Denis Koroskin <2korden gmail.com> wrote:

 On Mon, 08 Nov 2010 12:08:01 +0300, Simen kjaeraas  
 <simen.kjaras gmail.com> wrote:

 Eric Poggel <dnewsgroup2 yage3d.net> wrote:

 On 11/6/2010 6:50 AM, bearophile wrote:
 foobar:

 Any type can be wrapped by an OPTION type. trying to do the converse  
 of this is impractical and is bad design.

Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile

I still live in D1 land, so forgive me if I'm out of the loop--but what keeps this from being implemented at the library level as templated type: NotNull!(T) ? If there are limitations, maybe these areas of the language can be improved to get us there?

NotNull!T needs to have its default constructor disabled. That is the #1 blocker. There are other problems, mostly related to this - classes and structs with NotNull!T fields must define a constructor, and for structs that means the default destructor either needs to be possible to define or possible to disable. Arrays of NotNull!T may not be resized to a greater length, as that would require calling the default constructor of NotNull!T. I think that's it.

Not exactly disabled. The following should work: class Foo { this() { bar = new Object(); } NonNull!(Object) bar; // fine here } while the following shouldn't: NonNull!(Object) bar; // error here bar = new Object();

I belive you have misunderstood me. The NonNull struct would need to have its default constructor disabled. Then, extra logic would need to be added to class and struct constructors to ensure NonNull fields have their constructors called. Context-sensitive constructor disabling is a theoretical possibility, but seems to me to conflict with D's other goals. -- Simen
Nov 08 2010
prev sibling next sibling parent so <so so.do> writes:
 I studied the situation further. Now I've decided to leave D. I tried to  
 cope with all overly complex type system quirks, but have had enough of  
 it now. These two months with D truly opened my eyes. It means I won't  
 touch C++ or Java either.

 My next goal is to use an untyped (less types = better) language which  
 concentrates on cool syntax. Intensive test suites guarantee safety and  
 quality. An extreme version of TDD.

 I'm building an IDE (Eclipse) extension [in Java :( ] for automatically  
 injecting basic tests to typical programs. This way the tests only slow  
 down the debug build. The production version runs at maximum speed.

 Another solution is exploratory testing. I test stuff interactively  
 using a REPL. These reports and guidelines can be written down in .doc  
 word documents. I learnt this idea from Paul Graham and his new language.

Your choice is perfectly reasonable, you use static typed languages because there is no other way, and you need it. If you think a simpler language can do it for you, you don't need D like languages to begin with :) -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 08 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Thu, 25 Nov 2010 16:14:11 +0000
Bruno Medeiros <brunodomedeiros+spam com.gmail> wrote:

 What they don't realize, is that static typing is just compile-time=20
 unit-testing. :)

I tried to find some light on this topic a while ago, because similar argum= ents are constantly brought on the carpet, but I have never read any ration= ale, even less a report from an experimental study. Would be interesting; b= ut what is to measure, exactly? For a while, I have used nearly only dynamic languages; and I had bugs, som= e of them really naughty. Then about 2 years I passed on the stacic far mor= e bugs, most of them not naughty at all. The sheer wasx partly due to less = experience, but also because there are many more oportunities for bugs to c= ome up, from plain language complexity (not only of syntax). I start to appreciate static checking. During the first "static" year, I us= ed mainly freepascal and can say I had about _no_ type error -- what static= checking is here to catch, I guess. I think the reason was that during tho= se dynamic years I had developed a kind of unconscious type rigor both on t= he modeling & the implementation sides of the coin. These, I get many of them ;-) Wildly counted, after 1 hour typing without c= ompilation, I get a dozen compiler errors, 1 serious, 2-3 type errors, the = rest plain grammatical. I guess i'm slowly losing this purely mental typing= discipline: static checking makes my brain lazy ;-); it relies on the comp= iler instead of thinking clearly. Or it's just age... More seriously, I tried to classify programming errors. Let's say one need = to model dice. After all irrelevant aspect removed, just remains equiprobab= le alea in a natural interval. 1. Modeling error: bite hard -- using a distribution that does not provide = equiprobability. 2. Translation error: hurt as well -- random.int(1,6) actually returns in i= nterval [1,5]. 3. Implementation error: eg uint<-->int issues. Depending on the language, = this risk may be caught or not. (Yes, there should be a huge number of dice= with a huge number of faces...) 4. Grammatical error: except for beginners, harmless. Phase 2-->3 is, for me, where we pass from human (model) semantic to langua= ge semantics. I guess that's where type issues may come up. I'm aware this classification is personal, arbitrary, not based on any fact= . Just tried to think about the question: what kinds of bugs do Type Errors= reveal? Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 25 2010
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"foobar" <foo bar.com> wrote in message 
news:ib3a8k$1i58$1 digitalmars.com...
 1. the INVENTOR of the "reference" concept himself admits that this is a 
 flawed design.
 see: 
 http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake

First of all, "appeal to authority" is a logical fallacy. Second, there are plenty of cases where run-time nullability is useful and where lack of it is problematic at best: A tree or linked list, for example. The "null object" idiom doesn't count, because all it does is just reinvent null, and in a pointlessly roundabout way.
 2. "null" is an a type-system attribute, hence should be checked at 
 compile time and would have ZERO affect on run-time performance.
 Same as assigning a string value to an int variable.

I strongly agree with this. On a related note, I *hate* that D silently sticks in a default value whenever anything isn't properly inited. This is one thing where I really think C# got it right, and D got it wrong. And waving the "It's not leaving it with an undefined value like C does!" banner is an irritating strawman: Yea, it's better than C, but it still sucks. I also dislike that D's reference types being nullable by default is inconsistent with its value types. (Yea, naturally reference and value types are going to have inherent differences, but nullability shouldn't be one of them.)
 3. the notion of an "undefined" state for a type can be generally 
 implemented by the OPTION type and D pointers are an ad hoc implementation 
 of this concept.
 see: http://en.wikipedia.org/wiki/Option_type
 Any type can be wrapped by an OPTION type. trying to do the converse of 
 this is impractical and is bad design.

I strongly agree with this, too.
 4. As already pointed by others, Walter's array should be:
 Option!T[] array;

Only if you actually *want* to allow null elements.
 5. C programmers use null pointers and other special "canary" values as 
 Walter described mainly as rcodes. This is a BAD and bug-prone way of 
 handling errors and is replaced in D by a MUCH better mechanism called 
 "Exceptions".

Absolutely agree.
Nov 06 2010
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 06 November 2010 19:05:32 Nick Sabalausky wrote:
 "foobar" <foo bar.com> wrote in messagend in a pointlessly roundabout way.
 
 2. "null" is an a type-system attribute, hence should be checked at
 compile time and would have ZERO affect on run-time performance.
 Same as assigning a string value to an int variable.

I strongly agree with this. On a related note, I *hate* that D silently sticks in a default value whenever anything isn't properly inited. This is one thing where I really think C# got it right, and D got it wrong. And waving the "It's not leaving it with an undefined value like C does!" banner is an irritating strawman: Yea, it's better than C, but it still sucks.

Well, it _is_ better than C. Going C# or Java's route forces the programmer to initialize variables even in cases where they know that it's not necessary (which is annoying but may or may not be worth it), but more importantly (from Walter's perspective at least), it would require flow analysis, which he actively avoids. Using default values avoids memory bugs like you get in C and results in a simpler compiler implementation (and therefore a less bug-prone one) and makes it simpler for other tools to be written for the language. Now, it may be that Java and C#'s way is ultimately better, but unless you make a practice of declaring variables without initializing them (which generally should be avoided regardless), it generally doesn't matter. Also, it's essentially D's stance that not initializing a variable is a bug, so every variable is default initialized to the closest to an error value that exists for that type. null is the obvious choice for pointers and references. I'm moderately divided on the issue, but ultimately, I think that D's decision was a good one. Java and C#'s may or may not be better, but I still think that what D does works quite well.
 I also dislike that D's reference types being nullable by default is
 inconsistent with its value types. (Yea, naturally reference and value
 types are going to have inherent differences, but nullability shouldn't be
 one of them.)

It's not at all inconsistent if you look at from the perspective that types are default initialized to the closest thing to an error value that they have. Many of the value types (such as the integral types), don't really have a value that's an obvious error, so they don't fit in with that quite so well, but it's unavoidable given that they just don't have an obvious error value. And I don't understand why you think that nullability shouldn't be a difference between value types and reference types. That's one of the _key_ differences between them. Value types _cannot_ be null, while references can. And I'd sure hate to _not_ be able to have a null reference. It's irritating enough that arrays and associative arrays are almost treated the same when they're null as when they're empty. I can totally understand wanting non-nullable reference types. There are plenty of times where it just doesn't make sense to a have a variable which can be null - even if it's a reference - but there are plenty of cases where it _does_ make sense, and I do find the fact that D default initializes to error values to be quite useful, since I do consider it bad practice in general to not initialize a variable when it's declared. Sometimes you have to for scoping reasons or whatnot, but generally, variables _should_ be initialized when declared. - Jonathan M Davis
Nov 06 2010
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Jonathan M Davis wrote:
 Going C# or Java's route forces the programmer to 
 initialize variables even in cases where they know that it's not necessary 
 (which is annoying but may or may not be worth it),

Correct. It's not that doing flow analysis is hard, it's that it's impossible to do it correctly. So you wind up with wishy-washy messages that p "might not" be initialized, which is what the Java compiler does for this: class A { public void foo() { Object p; if (m) p = new Object(); if (m) p.toString(); // <-- p might not have been initialized } boolean m; } It even errors out if you write it as: class A { public void foo() { Object p; if (m) p = new Object(); if (p != null) // <-- p might not have been initialized p.toString(); } boolean m; } Note that the error message is on the null check!
Nov 06 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound2 digitalmars.com> wrote in message 
news:ib5bue$2ldp$1 digitalmars.com...
 Jonathan M Davis wrote:
 Going C# or Java's route forces the programmer to initialize variables 
 even in cases where they know that it's not necessary (which is annoying 
 but may or may not be worth it),

Correct. It's not that doing flow analysis is hard, it's that it's impossible to do it correctly. So you wind up with wishy-washy messages that p "might not" be initialized, which is what the Java compiler does for this: class A { public void foo() { Object p; if (m) p = new Object(); if (m) p.toString(); // <-- p might not have been initialized } boolean m; } It even errors out if you write it as: class A { public void foo() { Object p; if (m) p = new Object(); if (p != null) // <-- p might not have been initialized p.toString(); } boolean m; } Note that the error message is on the null check!

Since when should crap like that ever be written in the first place? In a code review, I'd slap both of those with a giant red "convoluted" stamp, *especially* if it's not just a trivial example like those. Besides, I'd much rather have easily-fixable false positives like that then the false negatives D gets now: Object p; if (m) p = new Object(); p.toString(); // Boom!, but *only* at run-time, and *only* if m just happens to be true. Plus, as I've argued before, I *wouldn't* want perfect flow analysis on that, I'd rather have easily-rememberable rules. If the initialization-safety of your code is dependent on complex logic, then you've written it wrong anyway. In simple examples like yours above, the fixes are not only obvious, but much more clear: Object p; if (m) { p = new Object(); p.toString(); } And in more complex cases, relying on complex logic to ensure things are inited properly is just wrong anyway, as I said above. Seriously, this whole "feature" amounts to nothing more than allowing the following *broken* code to occasionally get overlooked... Object p; if (m) p = new Object(); p.toString(); ...just for the completely non-existent "convenience" of writing crap like this... Object p; if (m) p = new Object(); if (m) p.toString(); ...instead of just doing it right: Object p; if (m) { p = new Object(); p.toString(); } You can label C#-style init-checking "wishy-washy" all you want, but that's still a hell of a lot better than "wrong", which is what D does (as evidenced by my first example above).
Nov 06 2010
parent "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:ib5ht0$2uff$1 digitalmars.com...
 "Walter Bright" <newshound2 digitalmars.com> wrote in message 
 news:ib5bue$2ldp$1 digitalmars.com...
 Jonathan M Davis wrote:
 Going C# or Java's route forces the programmer to initialize variables 
 even in cases where they know that it's not necessary (which is annoying 
 but may or may not be worth it),

Correct. It's not that doing flow analysis is hard, it's that it's impossible to do it correctly. So you wind up with wishy-washy messages that p "might not" be initialized, which is what the Java compiler does for this: class A { public void foo() { Object p; if (m) p = new Object(); if (m) p.toString(); // <-- p might not have been initialized } boolean m; } It even errors out if you write it as: class A { public void foo() { Object p; if (m) p = new Object(); if (p != null) // <-- p might not have been initialized p.toString(); } boolean m; } Note that the error message is on the null check!

Since when should crap like that ever be written in the first place? In a code review, I'd slap both of those with a giant red "convoluted" stamp, *especially* if it's not just a trivial example like those. Besides, I'd much rather have easily-fixable false positives like that then the false negatives D gets now: Object p; if (m) p = new Object(); p.toString(); // Boom!, but *only* at run-time, and *only* if m just happens to be true. Plus, as I've argued before, I *wouldn't* want perfect flow analysis on that, I'd rather have easily-rememberable rules. If the initialization-safety of your code is dependent on complex logic, then you've written it wrong anyway. In simple examples like yours above, the fixes are not only obvious, but much more clear: Object p; if (m) { p = new Object(); p.toString(); } And in more complex cases, relying on complex logic to ensure things are inited properly is just wrong anyway, as I said above. Seriously, this whole "feature" amounts to nothing more than allowing the following *broken* code to occasionally get overlooked... Object p; if (m) p = new Object(); p.toString(); ...just for the completely non-existent "convenience" of writing crap like this... Object p; if (m) p = new Object(); if (m) p.toString(); ...instead of just doing it right: Object p; if (m) { p = new Object(); p.toString(); } You can label C#-style init-checking "wishy-washy" all you want, but that's still a hell of a lot better than "wrong", which is what D does (as evidenced by my first example above).

Additionally, the root problem with default values is that they make deliberately-default-inited declarations and accidentally-uninited declarations completely indistinguishable by both the compiler and the programmer. (And no, "accidentally-uninited" does *not* imply "undefined value". If something's supposed be inited to X and it gets inited to Y, that's still *wrong* - *even* if it's reproducibly-wrong.)
Nov 06 2010
prev sibling parent reply Roman Ivanov <isroman.DEL ETE.km.ru> writes:
On 11/7/2010 1:02 AM, Walter Bright wrote:
 Jonathan M Davis wrote:
 Going C# or Java's route forces the programmer to initialize variables
 even in cases where they know that it's not necessary (which is
 annoying but may or may not be worth it),

Correct. It's not that doing flow analysis is hard, it's that it's impossible to do it correctly. So you wind up with wishy-washy messages that p "might not" be initialized, which is what the Java compiler does for this: class A { public void foo() { Object p; if (m) p = new Object(); if (m) p.toString(); // <-- p might not have been initialized } boolean m; } It even errors out if you write it as: class A { public void foo() { Object p; if (m) p = new Object(); if (p != null) // <-- p might not have been initialized p.toString(); } boolean m; } Note that the error message is on the null check!

I know what your mean, but the example is flawed: public void foo() { if (m) { Object p = new Object(); p.toString(); } } ... Thing is, I don't think you really _need_ flow analysis to implement nonnulity. The simplest way is to disallow creation of unitialized nonnull variables. I think it would work, but without some clever syntax such rule would be painful to follow for the programmers. Things like these would be prohibited, for example Duck d; if (b) d = new Duck(); else d = new DuckSubclass(); Seems like a big loss, but it really isn't. Duck d = b ? new Duck() : new DuckSubclass(); But ternary ops are not generic, unless you chain them which is super-ugly. Is there nothing that can fix this? Delegates probably can: enum Dri { one, two, three}; Dri s = Dri.two; int x = { if (s == Dri.one) return 1; else if (s == Dri.two) return 2; else return 3; }(); This works. I'm not sure how efficient it is, but that would solve the issue with initialization without any path analysis. If there was a clearer, more efficient syntax for this, it would work even better. int x = do { if (s == Dri.one) return 1; else if (s == Dri.two) return 2; else return 3; };
Nov 07 2010
next sibling parent Roman Ivanov <isroman.DEL ETE.km.ru> writes:
On 11/7/2010 10:51 AM, Roman Ivanov wrote:
 On 11/7/2010 1:02 AM, Walter Bright wrote:
 Jonathan M Davis wrote:
 Going C# or Java's route forces the programmer to initialize variables
 even in cases where they know that it's not necessary (which is
 annoying but may or may not be worth it),

Correct. It's not that doing flow analysis is hard, it's that it's impossible to do it correctly. So you wind up with wishy-washy messages that p "might not" be initialized, which is what the Java compiler does for this: class A { public void foo() { Object p; if (m) p = new Object(); if (m) p.toString(); // <-- p might not have been initialized } boolean m; } It even errors out if you write it as: class A { public void foo() { Object p; if (m) p = new Object(); if (p != null) // <-- p might not have been initialized p.toString(); } boolean m; } Note that the error message is on the null check!

I know what your mean, but the example is flawed: public void foo() { if (m) { Object p = new Object(); p.toString(); } } ... Thing is, I don't think you really _need_ flow analysis to implement nonnulity. The simplest way is to disallow creation of unitialized nonnull variables. I think it would work, but without some clever syntax such rule would be painful to follow for the programmers. Things like these would be prohibited, for example Duck d; if (b) d = new Duck(); else d = new DuckSubclass(); Seems like a big loss, but it really isn't. Duck d = b ? new Duck() : new DuckSubclass(); But ternary ops are not generic, unless you chain them which is super-ugly. Is there nothing that can fix this? Delegates probably can: enum Dri { one, two, three}; Dri s = Dri.two; int x = { if (s == Dri.one) return 1; else if (s == Dri.two) return 2; else return 3; }(); This works. I'm not sure how efficient it is, but that would solve the issue with initialization without any path analysis. If there was a clearer, more efficient syntax for this, it would work even better. int x = do { if (s == Dri.one) return 1; else if (s == Dri.two) return 2; else return 3; };

OF course, the code above assumes that Duck is unnulable. I didn't want to make my own syntax for it, because it would obfuscate the example.
Nov 07 2010
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.vls9bigxvxi10f biotronic-pc.lan...
 Roman Ivanov <isroman.DEL ete.km.ru> wrote:

 I know what your mean, but the example is flawed:

 public void foo()
 {
     if (m) {
          Object p = new Object();
          p.toString();
     }
 }

You misunderstand. The idea is this: void foo( ) { Object p; if ( m ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // 20 lines of code here if ( m ) { p.doSomethingWeird( dataFromAbove ); } }

If you do that, then there's two possibilities: A. You intended p to get inited on all code paths but forgot a codepath. With real init-checking the compiler will tell you and you can fix it. With D as it is, you're not informed at all, and you may or may not catch the problem before deployment. Obviously the former is better. B. You *intended* p to not always be inited, in which case the correct code is this: void foo( ) { Object p=null; if ( m ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // 20 lines of code here if ( p != null ) { p.doSomethingWeird( dataFromAbove ); } } And with by-default non-nullability and a good optional "Nullable" system, it would be something like this (Completely made-up syntax for "Nullable!Object", don't nitpick it), which would be far safer: void foo( ) { Nullable!Object p=null; if ( m ) { p = new Object( ); // Compiler knows p is really "Object" instead of "Nullable!Object" // from here to the end of scope, or until p is potentially reassigned. p.DoSomethingThatNeedsToBeDoneNow( ); } // 20 lines of code here p.toString(); // Compile-time error, p might be null // 20 lines of code here if ( p != null ) { // Compiler knows p is really "Object" instead of "Nullable!Object" inside here p.doSomethingWeird( dataFromAbove ); } }
Nov 07 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.vltiombuvxi10f biotronic-pc.lan...
 Nick Sabalausky <a a.a> wrote:
 If you do that, then there's two possibilities:

 A. You intended p to get inited on all code paths but forgot a codepath.
 With real init-checking the compiler will tell you and you can fix it. 
 With
 D as it is, you're not informed at all, and you may or may not catch the
 problem before deployment. Obviously the former is better.

 B. You *intended* p to not always be inited, in which case the correct 
 code
 is this:

 void foo( ) {
   Object p=null;
   if ( m ) {
     p = new Object( );
     p.DoSomethingThatNeedsToBeDoneNow( );
   }
   // 20 lines of code here
   if ( p != null ) {
     p.doSomethingWeird( dataFromAbove );
   }
 }

There is a third option, wherein the if condition will only be true if p !is null, but the actual condition is more complex: if ( m > 4 ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // code if ( m > 12 ) { p.doSomethingWeird( dataFromAbove ); }

- if ( m > 12 ) { + if ( p && m > 12 ) { And you can toss in an "if(m>12) assert(p);" if you're worried about that.
Nov 07 2010
parent "Nick Sabalausky" <a a.a> writes:
"Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.vltjpebhvxi10f biotronic-pc.lan...
 Nick Sabalausky <a a.a> wrote:

 - if ( m > 12 ) {
 + if ( p && m > 12 ) {

 And you can toss in an "if(m>12) assert(p);" if you're worried about 
 that.

Of course. But the point is, this is unnecessary. We know p !is null when m > 4.

And my point is, the need to write it the way you described is unnecessary and frankly, error prone. So if better safety can be obtained by ditching a useless style that really shouldn't be used anyway, I say we should go for it.
Nov 07 2010
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Simen kjaeraas wrote:
 You misunderstand. The idea is this:
 
 void foo( ) {
   Object p;
   if ( m ) {
     p = new Object( );
     p.DoSomethingThatNeedsToBeDoneNow( );
   }
   // 20 lines of code here
   if ( m ) {
     p.doSomethingWeird( dataFromAbove );
   }
 }

You're right, the real cases where this kind of thing occurs are much more complex. I just posted the thing boiled down. And, of course, there's always a way to refactor the code to eliminate the spurious error message. But sometimes the result is as ugly as Pascal's efforts to prove you really don't need a 'break' statement in a loop. The real problem with the spurious errors is that then people will put in an initialization "just to shut the compiler up." Time passes, and the next guy is looking at the code and wonders why x is being initialized to a value that is apparently never used, or worse, is initialized to some bogus value randomly picked by the long-retired programmer. I've seen code reviewers losing a lot of time on this issue. In general, I am opposed to inserting dead code and unused values to get the compiler to accept the program. It makes the code harder to reason about. Everything in the code should have a purpose that goes towards understanding the code. If there's: int x = 0; then it stands to reason that: 1. 0 is a valid value for whatever x is subsequently used for 2. this value of x is actually used When both of these are false, it is obfuscation and counterproductive.
Nov 07 2010
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound2 digitalmars.com> wrote in message 
news:ib783u$1odt$1 digitalmars.com...
 The real problem with the spurious errors is that then people will put in 
 an initialization "just to shut the compiler up." Time passes, and the 
 next guy is looking at the code and wonders why x is being initialized to 
 a value that is apparently never used, or worse, is initialized to some 
 bogus value randomly picked by the long-retired programmer. I've seen code 
 reviewers losing a lot of time on this issue.

 In general, I am opposed to inserting dead code and unused values to get 
 the compiler to accept the program.

Yes, you'd rather silently insert a value even when it's the wrong value.
Nov 07 2010
prev sibling parent reply Jussi Jumppanen <jussij zeuseedit.com> writes:
so Wrote:

 Not related to this but i have to share.
 Try compiling this in C/C++.
 
 int i = i + 5; // something like this.

void main() { int i = i + 5; // something like this. } Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. test.c c:\temp\test.c(3) : warning C4700: uninitialized local variable 'i' used
Nov 07 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Jussi Jumppanen" <jussij zeuseedit.com> wrote in message 
news:ib84l5$205p$1 digitalmars.com...
 so Wrote:

 Not related to this but i have to share.
 Try compiling this in C/C++.

 int i = i + 5; // something like this.

void main() { int i = i + 5; // something like this. } Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. test.c c:\temp\test.c(3) : warning C4700: uninitialized local variable 'i' used

Well I'll be damned, even C/C++ knows that uninitialized variables shouldn't be used, and yet D doesn't. This is where Walter claims that getting rid of that warning improves safety because it prevents people from "shutting the compiler up" by changing this: int i = i + 5; Into this code which D happily accepts: int i; i = i + 5;
Nov 07 2010
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Nick Sabalausky wrote:
 Well I'll be damned, even C/C++ knows that uninitialized variables shouldn't 
 be used, and yet D doesn't. This is where Walter claims that getting rid of 
 that warning improves safety because it prevents people from "shutting the 
 compiler up" by changing this:
 
 int i = i + 5;
 
 Into this code which D happily accepts:
 
 int i;
 i = i + 5;

For the code: void test() { int i = i + 5; } D reports: test.d(4): Error: undefined identifier i
Nov 07 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound2 digitalmars.com> wrote in message 
news:ib87ai$26u8$1 digitalmars.com...
 Nick Sabalausky wrote:
 Well I'll be damned, even C/C++ knows that uninitialized variables 
 shouldn't be used, and yet D doesn't. This is where Walter claims that 
 getting rid of that warning improves safety because it prevents people 
 from "shutting the compiler up" by changing this:

 int i = i + 5;

 Into this code which D happily accepts:

 int i;
 i = i + 5;

For the code: void test() { int i = i + 5; } D reports: test.d(4): Error: undefined identifier i

Yes, which by your "people will toss something in to shut the compiler up" reasoning will then cause people to turn that into this "to shut the compiler up": void test() { int i; i = i + 5; } Which the D compiler will be perfectly happy with even though it's probably wrong.
Nov 08 2010
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Nick Sabalausky wrote:
 "Walter Bright" <newshound2 digitalmars.com> wrote in message 
 For the code:

   void test()
   {
     int i = i + 5;
   }

 D reports:

   test.d(4): Error: undefined identifier i

Yes, which by your "people will toss something in to shut the compiler up" reasoning will then cause people to turn that into this "to shut the compiler up": void test() { int i; i = i + 5; } Which the D compiler will be perfectly happy with even though it's probably wrong.

I see no reason why people would do that. It doesn't save typing, it isn't convenient, etc.
Nov 08 2010
parent Walter Bright <newshound2 digitalmars.com> writes:
Walter Bright wrote:
 I see no reason why people would do that. It doesn't save typing, it 
 isn't convenient, etc.

What I mean is that I've never seen anyone do that, and I've seen a lot of junk people did to shut the compiler up.
Nov 08 2010
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"so" <so so.do> wrote in message news:op.vlubxy2a7dtt59 so-pc...
 int i;
 i = i + 5;

I can't see any uninitialized var there, can you? :P

There's no usage of an undeclared variable, but the right-hand-side of the second line uses 'i' before *the programmer* initializes it. Yes, the D compiler chooses to automatically initialize it, but by doing so it silently creates a bug every time the programmer intends 'i' to start out as anything other than 0. And it's not easily noticed since 0 is a commonly-used value. (Something like 0xDEADBEEF would at least be an improvement (albeit a small one) since at least that would stand out more and likely fail more spectacularly.)
Nov 08 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"so" <so so.do> wrote in message news:op.vlv3iukp7dtt59 so-pc...
 There's no usage of an undeclared variable, but the right-hand-side of 
 the
 second line uses 'i' before *the programmer* initializes it. Yes, the D
 compiler chooses to automatically initialize it, but by doing so it 
 silently
 creates a bug every time the programmer intends 'i' to start out as 
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used 
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a 
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

So you want language force you to type either "int x=0;" or "int x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important.

I tend to get a bit fired up by it because Walter's reasoning on it being *better* to automatically assume some init value baffles me.
 Again, for both coder and the code-reader there is no ambiguity here, 
 every single thing is defined.

Whether or not it's defined isn't really the issue here (although if it weren't defined, that would definitely be a problem). It just comes down to "more error-prone" vs "less error-prone".
Nov 08 2010
parent reply Daniel Gibson <metalcaedes gmail.com> writes:
Nick Sabalausky schrieb:
 "so" <so so.do> wrote in message news:op.vlv3iukp7dtt59 so-pc...
 There's no usage of an undeclared variable, but the right-hand-side of 
 the
 second line uses 'i' before *the programmer* initializes it. Yes, the D
 compiler chooses to automatically initialize it, but by doing so it 
 silently
 creates a bug every time the programmer intends 'i' to start out as 
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used 
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a 
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important.

I tend to get a bit fired up by it because Walter's reasoning on it being *better* to automatically assume some init value baffles me.

It gives deterministic results/errors. For example, when your code works when an int is initialized with 0 (but you didn't initialize it), it may work most of the time in C and fail randomly. In D it will always work. Same thing the other way round. Or if you do some calculation with an uninitialized int value.. I guess 0 is one of the easiest values to spot: on multiplication it creates 0 and on addition it doesn't change to value so by looking at the unwanted result of a calculation you probably can see the error more easily than on some other value (or even a random value, that may create results that look about right).
Nov 09 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Daniel Gibson" <metalcaedes gmail.com> wrote in message 
news:ibbp25$ls8$1 digitalmars.com...
 Nick Sabalausky schrieb:
 "so" <so so.do> wrote in message news:op.vlv3iukp7dtt59 so-pc...
 There's no usage of an undeclared variable, but the right-hand-side of 
 the
 second line uses 'i' before *the programmer* initializes it. Yes, the D
 compiler chooses to automatically initialize it, but by doing so it 
 silently
 creates a bug every time the programmer intends 'i' to start out as 
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used 
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a 
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important.

I tend to get a bit fired up by it because Walter's reasoning on it being *better* to automatically assume some init value baffles me.

It gives deterministic results/errors. For example, when your code works when an int is initialized with 0 (but you didn't initialize it), it may work most of the time in C and fail randomly. In D it will always work. Same thing the other way round. Or if you do some calculation with an uninitialized int value.. I guess 0 is one of the easiest values to spot: on multiplication it creates 0 and on addition it doesn't change to value so by looking at the unwanted result of a calculation you probably can see the error more easily than on some other value (or even a random value, that may create results that look about right).

Where are people getting the idea that I've said C's behavior is better than D's? Once again, I'm not talking about D vs C (ie "int i;" leaves 'i' in an undefined state), I'm talking about D vs C# (ie, "int i;" causes compile-time errors when 'i' is read before being written to).
Nov 09 2010
parent "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:ibc4h2$1mmn$1 digitalmars.com...
 "Daniel Gibson" <metalcaedes gmail.com> wrote in message 
 news:ibbp25$ls8$1 digitalmars.com...
 Nick Sabalausky schrieb:
 "so" <so so.do> wrote in message news:op.vlv3iukp7dtt59 so-pc...
 There's no usage of an undeclared variable, but the right-hand-side of 
 the
 second line uses 'i' before *the programmer* initializes it. Yes, the 
 D
 compiler chooses to automatically initialize it, but by doing so it 
 silently
 creates a bug every time the programmer intends 'i' to start out as 
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used 
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a 
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important.

I tend to get a bit fired up by it because Walter's reasoning on it being *better* to automatically assume some init value baffles me.

It gives deterministic results/errors. For example, when your code works when an int is initialized with 0 (but you didn't initialize it), it may work most of the time in C and fail randomly. In D it will always work. Same thing the other way round. Or if you do some calculation with an uninitialized int value.. I guess 0 is one of the easiest values to spot: on multiplication it creates 0 and on addition it doesn't change to value so by looking at the unwanted result of a calculation you probably can see the error more easily than on some other value (or even a random value, that may create results that look about right).

Where are people getting the idea that I've said C's behavior is better than D's? Once again, I'm not talking about D vs C (ie "int i;" leaves 'i' in an undefined state), I'm talking about D vs C# (ie, "int i;" causes compile-time errors when 'i' is read before being written to).

Again, saying "Not giving compile-time errors on reading before writing is good because C leaves values in an undefined state" **makes no sense**. It's a complete non-sequitor.
Nov 09 2010
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.vlwul8tuvxi10f biotronic-pc.lan...
 Daniel Gibson <metalcaedes gmail.com> wrote:

 Nick Sabalausky schrieb:
 "so" <so so.do> wrote in message news:op.vlv3iukp7dtt59 so-pc...
 There's no usage of an undeclared variable, but the right-hand-side 
 of the
 second line uses 'i' before *the programmer* initializes it. Yes, the 
 D
 compiler chooses to automatically initialize it, but by doing so it 
 silently
 creates a bug every time the programmer intends 'i' to start out as 
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used 
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a 
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important.

being *better* to automatically assume some init value baffles me.

It gives deterministic results/errors.

Yup. Also, as opposed to certain other solutions, it does not require advanced flow control, that is likely to be incomplete. Incomplete flow control here will make people write code 'to shut the compiler up'. And that is worse than uninitialized variables.

First of all, the risks from "shut the compiler up" initalizations are highly exagerated (I've dealt with enough C# to know that Walter's full of crap on the "dangers" of that). Secondly, there is absolutely no way it's worse than auto-initing. Let's look at an example: int i; // Dumb-ass convoluted code that should never pass code review anyway: if(m) i = something; // Stuff here if(m) // Do something with 'i' Suppose the compiler complains 'i' might be used before being written to. The programmer has two choices: 1. Blindly toss in "=0" with no regard as to whether or not it's correct. 2. Fix the damn code properly (And believe it or not, this *may* actually amount to "int i=0;", but just not always). Now, let's go back to D's current behavior. Only one thing ever happens now. The compiler will: 1. Blindly toss in "=0" with no regard as to whether or not it's correct. Note that's word-for-word identical to before, except now option 2, the *right* option, doesn't even fucking exist. That does NOT make it better, that makes it worse.
Nov 09 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:ibc5ve$1q75$1 digitalmars.com...
 "Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
 news:op.vlwul8tuvxi10f biotronic-pc.lan...
 Yup. Also, as opposed to certain other solutions, it does not require
 advanced flow control, that is likely to be incomplete. Incomplete flow
 control here will make people write code 'to shut the compiler up'. And
 that is worse than uninitialized variables.

First of all, the risks from "shut the compiler up" initalizations are highly exagerated (I've dealt with enough C# to know that Walter's full of crap on the "dangers" of that). Secondly, there is absolutely no way it's worse than auto-initing. Let's look at an example: int i; // Dumb-ass convoluted code that should never pass code review anyway: if(m) i = something; // Stuff here if(m) // Do something with 'i'

Also, as I've said when this was discussed in the past, I wouldn't even want the flow analysis to be perfect because all that would accomplish is to encourage garbage like the above (which would just end up being highly fragile anway).
 Suppose the compiler complains 'i' might be used before being written to. 
 The programmer has two choices:

 1. Blindly toss in "=0" with no regard as to whether or not it's correct.
 2. Fix the damn code properly (And believe it or not, this *may* actually 
 amount to "int i=0;", but just not always).

 Now, let's go back to D's current behavior. Only one thing ever happens 
 now. The compiler will:

 1. Blindly toss in "=0" with no regard as to whether or not it's correct.

 Note that's word-for-word identical to before, except now option 2, the 
 *right* option, doesn't even fucking exist. That does NOT make it better, 
 that makes it worse.

 

Nov 09 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
I agree with Nick Sabalausky in this discussion.

Simen kjaeraas:

 But if it isn't, we must either define what it should do, or accept that
 some compilers will catch some of the uninitialized variable bugs, where
 others do not. We do not want that.

Right. There are ways to write formal specs for this too But it's not an easy thing to do. D has chosen to have simpler implementation and specs. This means that on this regard implementing a correct D front-end is simpler than implementing a C# front-end. Bye, bearophile
Nov 09 2010
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Simen kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.vlw3ohvavxi10f biotronic-pc.lan...
 Nick Sabalausky <a a.a> wrote:

 Also, as I've said when this was discussed in the past, I wouldn't even 
 want
 the flow analysis to be perfect because all that would accomplish is to
 encourage garbage like the above (which would just end up being highly
 fragile anway).

But if it isn't, we must either define what it should do, or accept that some compilers will catch some of the uninitialized variable bugs, where others do not. We do not want that.

Agreed.
Nov 09 2010
prev sibling parent Jussi Jumppanen <jussij zeusedit.com> writes:
so Wrote:

 At initialization rvalue should not contain anything about 
 lvalue, this is absurd.

That may well be the case. I was only pointing out that as far as the Microsoft compiler is concerned, saying the code: 'compiles with no warning, no error, nothing...' is incorrect. It will issue a warning even if the default warning level of 1 is used. Now as to question whether or not this is a smart way to handle the situation, that is a totally different discussion all together;)
Nov 08 2010
prev sibling next sibling parent "Nick Sabalausky" <a a.a> writes:
"Jonathan M Davis" <jmdavisProg gmx.com> wrote in message 
news:mailman.144.1289103661.21107.digitalmars-d puremagic.com...
 On Saturday 06 November 2010 19:05:32 Nick Sabalausky wrote:
 "foobar" <foo bar.com> wrote in messagend in a pointlessly roundabout 
 way.

 2. "null" is an a type-system attribute, hence should be checked at
 compile time and would have ZERO affect on run-time performance.
 Same as assigning a string value to an int variable.

I strongly agree with this. On a related note, I *hate* that D silently sticks in a default value whenever anything isn't properly inited. This is one thing where I really think C# got it right, and D got it wrong. And waving the "It's not leaving it with an undefined value like C does!" banner is an irritating strawman: Yea, it's better than C, but it still sucks.

Now, it may be that Java and C#'s way is ultimately better, but unless you make a practice of declaring variables without initializing them (which generally should be avoided regardless), it generally doesn't matter.

The problem is values accidentally not being inited. When that happens, D jumps in and just assumes it should be xxxx, which is not always correct. "null" is not always the intended starting value for a reference type. 0 is not always the intended starting value for an integer type. Invalid-code-unit is not always the intended starting value for a character. NaN is not always the intended starting value for a floating point type, and while it *is* better than, for example, 0 for ints, it's still tends to leave the error undetected until further down the code-path (same for null).
 Also, it's essentially D's stance that not initializing a variable is a 
 bug, so
 every variable is default initialized to the closest to an error value 
 that
 exists for that type.

So why respond to a bug by ignoring it and occasionally turning it into another bug? If it considers something a bug, then it should *say* so.
 It's not at all inconsistent if you look at from the perspective that 
 types are
 default initialized to the closest thing to an error value that they have. 
 Many
 of the value types (such as the integral types), don't really have a value
 that's an obvious error, so they don't fit in with that quite so well, but 
 it's
 unavoidable given that they just don't have an obvious error value.

See, that goes back to what the OP was saying, and I agree with: "Error" should *not* be a valid value for a type (unless explicitly decreed by the programmer for a specific variable). It should either be a value that the programmer explicitly *gives* it, or a compile-time error.
 And I don't understand why you think that nullability shouldn't be a 
 difference
 between value types and reference types. That's one of the _key_ 
 differences
 between them. Value types _cannot_ be null, while references can. And I'd 
 sure
 hate to _not_ be able to have a null reference.

It's only a difference in D because D makes it so. It's *not* a fundamental difference of the concept of a "reference type" and "value type". Non-nullable reference types can and do exist. And Haxe has value types that are nullable. I thnk JS does too. And I never suggested we not be able to have null references at all. In fact, I already made a big point that they *should* be allowed: "there are plenty of cases where run-time nullability is useful and where lack of it is problematic at best: A tree or linked list, for example. The "null object" idiom doesn't count, because all it does is just reinvent null, and in a pointlessly roundabout way." It just shouldn't be default, and it should only be used when it's actually needed.
 I can totally understand wanting non-nullable reference types. There are 
 plenty
 of times where it just doesn't make sense to a have a variable which can 
 be null
 - even if it's a reference - but there are plenty of cases where it _does_ 
 make
 sense,

Agreed. And I've also come across plenty of cases where nullable value types are useful (but obviously they shouldn't be the default). Conceptually, nullability is orthogonal from reference-vs-value, but many languages conflate the two (presumably because it just happens to be the easiest due to the way the hardware typically works).
Nov 07 2010
prev sibling parent reply foobar <foo bar.com> writes:
Jonathan M Davis Wrote:

 On Saturday 06 November 2010 19:05:32 Nick Sabalausky wrote:
 "foobar" <foo bar.com> wrote in messagend in a pointlessly roundabout way.
 
 2. "null" is an a type-system attribute, hence should be checked at
 compile time and would have ZERO affect on run-time performance.
 Same as assigning a string value to an int variable.

I strongly agree with this. On a related note, I *hate* that D silently sticks in a default value whenever anything isn't properly inited. This is one thing where I really think C# got it right, and D got it wrong. And waving the "It's not leaving it with an undefined value like C does!" banner is an irritating strawman: Yea, it's better than C, but it still sucks.

Well, it _is_ better than C. Going C# or Java's route forces the programmer to initialize variables even in cases where they know that it's not necessary (which is annoying but may or may not be worth it), but more importantly (from Walter's perspective at least), it would require flow analysis, which he actively avoids. Using default values avoids memory bugs like you get in C and results in a simpler compiler implementation (and therefore a less bug-prone one) and makes it simpler for other tools to be written for the language. Now, it may be that Java and C#'s way is ultimately better, but unless you make a practice of declaring variables without initializing them (which generally should be avoided regardless), it generally doesn't matter. Also, it's essentially D's stance that not initializing a variable is a bug, so every variable is default initialized to the closest to an error value that exists for that type. null is the obvious choice for pointers and references. I'm moderately divided on the issue, but ultimately, I think that D's decision was a good one. Java and C#'s may or may not be better, but I still think that what D does works quite well.
 I also dislike that D's reference types being nullable by default is
 inconsistent with its value types. (Yea, naturally reference and value
 types are going to have inherent differences, but nullability shouldn't be
 one of them.)

It's not at all inconsistent if you look at from the perspective that types are default initialized to the closest thing to an error value that they have. Many of the value types (such as the integral types), don't really have a value that's an obvious error, so they don't fit in with that quite so well, but it's unavoidable given that they just don't have an obvious error value. And I don't understand why you think that nullability shouldn't be a difference between value types and reference types. That's one of the _key_ differences between them. Value types _cannot_ be null, while references can. And I'd sure hate to _not_ be able to have a null reference. It's irritating enough that arrays and associative arrays are almost treated the same when they're null as when they're empty. I can totally understand wanting non-nullable reference types. There are plenty of times where it just doesn't make sense to a have a variable which can be null - even if it's a reference - but there are plenty of cases where it _does_ make sense, and I do find the fact that D default initializes to error values to be quite useful, since I do consider it bad practice in general to not initialize a variable when it's declared. Sometimes you have to for scoping reasons or whatnot, but generally, variables _should_ be initialized when declared. - Jonathan M Davis

Both the current D way and the C# way are ugly hacks. Ideally you should have TWO types: T and Option!T (ignore syntax for now). Most of the time you would use: auto variable = new T(params); // no need for nulls here! and for the situations where you actually NEED to have a non-initialized variable you'd use: Option!T variable; // look ma, explicitly asking for a nullable variable Simple, ain't it? And it supports Walter's convoluted examples AND is explicit about it so it prevents illegal operations at compile time.
Nov 07 2010
parent reply steveh <steveh57 hotmai.l> writes:
foobar Wrote:

 Jonathan M Davis Wrote:
 
 On Saturday 06 November 2010 19:05:32 Nick Sabalausky wrote:
 "foobar" <foo bar.com> wrote in messagend in a pointlessly roundabout way.
 
 2. "null" is an a type-system attribute, hence should be checked at
 compile time and would have ZERO affect on run-time performance.
 Same as assigning a string value to an int variable.

I strongly agree with this. On a related note, I *hate* that D silently sticks in a default value whenever anything isn't properly inited. This is one thing where I really think C# got it right, and D got it wrong. And waving the "It's not leaving it with an undefined value like C does!" banner is an irritating strawman: Yea, it's better than C, but it still sucks.

Well, it _is_ better than C. Going C# or Java's route forces the programmer to initialize variables even in cases where they know that it's not necessary (which is annoying but may or may not be worth it), but more importantly (from Walter's perspective at least), it would require flow analysis, which he actively avoids. Using default values avoids memory bugs like you get in C and results in a simpler compiler implementation (and therefore a less bug-prone one) and makes it simpler for other tools to be written for the language. Now, it may be that Java and C#'s way is ultimately better, but unless you make a practice of declaring variables without initializing them (which generally should be avoided regardless), it generally doesn't matter. Also, it's essentially D's stance that not initializing a variable is a bug, so every variable is default initialized to the closest to an error value that exists for that type. null is the obvious choice for pointers and references. I'm moderately divided on the issue, but ultimately, I think that D's decision was a good one. Java and C#'s may or may not be better, but I still think that what D does works quite well.
 I also dislike that D's reference types being nullable by default is
 inconsistent with its value types. (Yea, naturally reference and value
 types are going to have inherent differences, but nullability shouldn't be
 one of them.)

It's not at all inconsistent if you look at from the perspective that types are default initialized to the closest thing to an error value that they have. Many of the value types (such as the integral types), don't really have a value that's an obvious error, so they don't fit in with that quite so well, but it's unavoidable given that they just don't have an obvious error value. And I don't understand why you think that nullability shouldn't be a difference between value types and reference types. That's one of the _key_ differences between them. Value types _cannot_ be null, while references can. And I'd sure hate to _not_ be able to have a null reference. It's irritating enough that arrays and associative arrays are almost treated the same when they're null as when they're empty. I can totally understand wanting non-nullable reference types. There are plenty of times where it just doesn't make sense to a have a variable which can be null - even if it's a reference - but there are plenty of cases where it _does_ make sense, and I do find the fact that D default initializes to error values to be quite useful, since I do consider it bad practice in general to not initialize a variable when it's declared. Sometimes you have to for scoping reasons or whatnot, but generally, variables _should_ be initialized when declared. - Jonathan M Davis

Both the current D way and the C# way are ugly hacks. Ideally you should have TWO types: T and Option!T (ignore syntax for now). Most of the time you would use: auto variable = new T(params); // no need for nulls here! and for the situations where you actually NEED to have a non-initialized variable you'd use: Option!T variable; // look ma, explicitly asking for a nullable variable Simple, ain't it? And it supports Walter's convoluted examples AND is explicit about it so it prevents illegal operations at compile time.

Sounds more retarded than the notorious 'retard' here. It's because of people like u that D3 might not come. If you disagree too much with AA and WB they have no interest to make D3. This nonnull question might be good place to give up.
Nov 07 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/7/10 9:08 AM, Denis Koroskin wrote:
 On Sun, 07 Nov 2010 18:01:32 +0300, steveh <steveh57 hotmai.l> wrote:

 Sounds more retarded than the notorious 'retard' here. It's because of
 people like u that D3 might not come. If you disagree too much with AA
 and WB they have no interest to make D3. This nonnull question might
 be good place to give up.

Cool down and relax! His post wasn't actually that bad.

Clearly it is the preference of some that non-null references are the default (so then one can use e.g. an option datatype to model nullable references). It is also the preference of some that values are immutable by default. I think it's too late to change such defaults at this point. I think it's reasonable to let the current references and pointers continue to be as they are, and work on disable (particularly its interaction with constructors) to make it easy to implement restricted subsets of values. Then NonNull would be a useful library artifact among other ones. Andrei
Nov 07 2010
parent "Wyrlon" <wyrl gmx.net> writes:
 I think it's reasonable to let the current references and pointers 
 continue to be as they are, and work on  disable (particularly its 
 interaction with constructors) to make it easy to implement restricted 
 subsets of values. Then NonNull would be a useful library artifact among 
 other ones.


 Andrei

Rather than focusing solely at NonNull, I'd prefer to return to the problem at hand and ask what alternate solutions are there? For that reason I really like Andrei's approach, as it has far wider appliances. Still I'd like to point out one other idea which I came across in the programming language 'E', it's extemely simple to implement yet solves many other problems which nonnull cannot. http://blubbedev.net/e_guide_html/ch_13c.html As I see it, the main problem is interoperability between 3rd party libraries... in contrast to within ones own framwork/program since 'D' already provides first class error handling mechanisms. Basically it's just a "global" list of "function error return codes" translated into the exception of your choice, sure it is possible to wrap everything manually... but this mechanism makes it very easy to do the right thing and _keep_ the old naming convention of the 3rd party library and still benefit from improved automatic error handling... Teoretically one could specify it already when importing the function with "extern C" etc... but it probably would require too many manual adjustments to be really useful... well just _throwing_ the idea in here. ;) Wyrlon
Nov 07 2010
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"steveh" <steveh57 hotmai.l> wrote in message 
news:ib6f0c$2s4o$1 digitalmars.com...
 Sounds more retarded than the notorious 'retard' here. It's because of 
 people like u that D3 might not come. If you disagree too much with AA and 
 WB they have no interest to make D3. This nonnull question might be good 
 place to give up.

Ridiculous argument. Even if Walter and Andrei were that petty (they're not), then not pushing important changes like this would render any D3 pointless anyway.
Nov 07 2010
prev sibling next sibling parent reply foobar <foo bar.com> writes:
Nick Sabalausky Wrote:

 "foobar" <foo bar.com> wrote in message 
 news:ib3a8k$1i58$1 digitalmars.com...
 1. the INVENTOR of the "reference" concept himself admits that this is a 
 flawed design.
 see: 
 http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake

First of all, "appeal to authority" is a logical fallacy. Second, there are plenty of cases where run-time nullability is useful and where lack of it is problematic at best: A tree or linked list, for example. The "null object" idiom doesn't count, because all it does is just reinvent null, and in a pointlessly roundabout way.

You seem to contradict yourself a bit here. As you pointed out yourself bellow, if you really *want* to use nullable types than you should explicitly use option!T instead of T. This is how those data structures you mentioned are implemented in languages such as ML. e.g. datatype Tree = Leaf | Tree of ('a * Tree * Tree) usage example: fun sum Leaf = 0 | sum Tree(val, left , right) = val + sum (left) + sum (right) The important point here is that it won't COMPILE without the first part that handles the Leaf. This makes the use of null explicit and the compiler will verify that you are using nulls correctly. In current D, there is no check to prevent compiling of: int sum(Tree tree) { // doesn't handle case of null tree !!! return tree.value + sum(tree.left) + sum(tree.right); } This is not the OO null object idiom and there's no re-invention of null here since I haven't discussed the implementation of Option(T) at all. IMO it should be a language built-in just like const is in order to get the full benefits like in ML.
 
 2. "null" is an a type-system attribute, hence should be checked at 
 compile time and would have ZERO affect on run-time performance.
 Same as assigning a string value to an int variable.

I strongly agree with this. On a related note, I *hate* that D silently sticks in a default value whenever anything isn't properly inited. This is one thing where I really think C# got it right, and D got it wrong. And waving the "It's not leaving it with an undefined value like C does!" banner is an irritating strawman: Yea, it's better than C, but it still sucks. I also dislike that D's reference types being nullable by default is inconsistent with its value types. (Yea, naturally reference and value types are going to have inherent differences, but nullability shouldn't be one of them.)
 3. the notion of an "undefined" state for a type can be generally 
 implemented by the OPTION type and D pointers are an ad hoc implementation 
 of this concept.
 see: http://en.wikipedia.org/wiki/Option_type
 Any type can be wrapped by an OPTION type. trying to do the converse of 
 this is impractical and is bad design.

I strongly agree with this, too.
 4. As already pointed by others, Walter's array should be:
 Option!T[] array;

Only if you actually *want* to allow null elements.
 5. C programmers use null pointers and other special "canary" values as 
 Walter described mainly as rcodes. This is a BAD and bug-prone way of 
 handling errors and is replaced in D by a MUCH better mechanism called 
 "Exceptions".

Absolutely agree.

Nov 07 2010
parent "Nick Sabalausky" <a a.a> writes:
"foobar" <foo bar.com> wrote in message news:ib5l2a$1v9$1 digitalmars.com...
 Nick Sabalausky Wrote:

 "foobar" <foo bar.com> wrote in message
 news:ib3a8k$1i58$1 digitalmars.com...
 1. the INVENTOR of the "reference" concept himself admits that this is 
 a
 flawed design.
 see:
 http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake

First of all, "appeal to authority" is a logical fallacy. Second, there are plenty of cases where run-time nullability is useful and where lack of it is problematic at best: A tree or linked list, for example. The "null object" idiom doesn't count, because all it does is just reinvent null, and in a pointlessly roundabout way.

You seem to contradict yourself a bit here.

It's been awhile since I read that article you linked to, but from what I remember, it sounded to me like he was saying that nullability period was bad, and that there should never be any nulls (but maybe I'm just misremembering). So I thought you were also saying that there should not be any nullability.
As you pointed out yourself bellow, if you really *want* to use nullable 
types than you should explicitly use option!T instead of T.

Yup, I think we completely agree on everything here.
Nov 07 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Sun, 07 Nov 2010 07:20:10 -0500
foobar <foo bar.com> wrote:

 Simple, ain't it? And it supports Walter's convoluted examples AND is exp=

... AND it makes to safe case default ;-) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 07 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Sun, 07 Nov 2010 01:54:24 -0500, Nick Sabalausky wrote:

 "Nick Sabalausky" <a a.a> wrote in message
 news:ib5ht0$2uff$1 digitalmars.com...
 "Walter Bright" <newshound2 digitalmars.com> wrote in message
 news:ib5bue$2ldp$1 digitalmars.com...
 Jonathan M Davis wrote:
 Going C# or Java's route forces the programmer to initialize
 variables even in cases where they know that it's not necessary
 (which is annoying but may or may not be worth it),

Correct. It's not that doing flow analysis is hard, it's that it's impossible to do it correctly. So you wind up with wishy-washy messages that p "might not" be initialized, which is what the Java compiler does for this: class A { public void foo() { Object p; if (m) p = new Object(); if (m) p.toString(); // <-- p might not have been initialized } boolean m; } It even errors out if you write it as: class A { public void foo() { Object p; if (m) p = new Object(); if (p != null) // <-- p might not have been initialized p.toString(); } boolean m; } Note that the error message is on the null check!

Since when should crap like that ever be written in the first place? In a code review, I'd slap both of those with a giant red "convoluted" stamp, *especially* if it's not just a trivial example like those. Besides, I'd much rather have easily-fixable false positives like that then the false negatives D gets now: Object p; if (m) p = new Object(); p.toString(); // Boom!, but *only* at run-time, and *only* if m just happens to be true. Plus, as I've argued before, I *wouldn't* want perfect flow analysis on that, I'd rather have easily-rememberable rules. If the initialization-safety of your code is dependent on complex logic, then you've written it wrong anyway. In simple examples like yours above, the fixes are not only obvious, but much more clear: Object p; if (m) { p = new Object(); p.toString(); } And in more complex cases, relying on complex logic to ensure things are inited properly is just wrong anyway, as I said above. Seriously, this whole "feature" amounts to nothing more than allowing the following *broken* code to occasionally get overlooked... Object p; if (m) p = new Object(); p.toString(); ...just for the completely non-existent "convenience" of writing crap like this... Object p; if (m) p = new Object(); if (m) p.toString(); ...instead of just doing it right: Object p; if (m) { p = new Object(); p.toString(); } You can label C#-style init-checking "wishy-washy" all you want, but that's still a hell of a lot better than "wrong", which is what D does (as evidenced by my first example above).

deliberately-default-inited declarations and accidentally-uninited declarations completely indistinguishable by both the compiler and the programmer. (And no, "accidentally-uninited" does *not* imply "undefined value". If something's supposed be inited to X and it gets inited to Y, that's still *wrong* - *even* if it's reproducibly-wrong.)

When I started with D, some of the main reasons for choosing D were: - you can return int from a void function without compilation errors - you can use 'void main()' instead of 'int main()' and a silly return value 0 - counter variables are default initialized to 0 I thought it would save so much typing.
Nov 07 2010
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 07 Nov 2010 18:01:32 +0300, steveh <steveh57 hotmai.l> wrote:

 Sounds more retarded than the notorious 'retard' here. It's because of  
 people like u that D3 might not come. If you disagree too much with AA  
 and WB they have no interest to make D3. This nonnull question might be  
 good place to give up.

Cool down and relax! His post wasn't actually that bad.
Nov 07 2010
prev sibling next sibling parent reply so <so so.do> writes:
 On a related note, I *hate* that D silently sticks in a default value
 whenever anything isn't properly inited. This is one thing where I really
 think C# got it right, and D got it wrong. And waving the "It's not  
 leaving
 it with an undefined value like C does!" banner is an irritating  
 strawman:
 Yea, it's better than C, but it still sucks.

This is one of the times (and this doesn't happen rarely) i am glad Walter is the head of D. It is total BS i am sorry can put it in a polite way. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"so" <so so.do> wrote in message news:op.vls71ytk7dtt59 so-pc...
 On a related note, I *hate* that D silently sticks in a default value
 whenever anything isn't properly inited. This is one thing where I really
 think C# got it right, and D got it wrong. And waving the "It's not 
 leaving
 it with an undefined value like C does!" banner is an irritating 
 strawman:
 Yea, it's better than C, but it still sucks.

This is one of the times (and this doesn't happen rarely) i am glad Walter is the head of D. It is total BS i am sorry can put it in a polite way.

How is it total BS?
Nov 07 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"so" <so so.do> wrote in message news:op.vltk19pq7dtt59 so-pc...
 On Sun, 07 Nov 2010 22:41:41 +0200, Nick Sabalausky <a a.a> wrote:

 "so" <so so.do> wrote in message news:op.vls71ytk7dtt59 so-pc...
 On a related note, I *hate* that D silently sticks in a default value
 whenever anything isn't properly inited. This is one thing where I 
 really
 think C# got it right, and D got it wrong. And waving the "It's not
 leaving
 it with an undefined value like C does!" banner is an irritating
 strawman:
 Yea, it's better than C, but it still sucks.

This is one of the times (and this doesn't happen rarely) i am glad Walter is the head of D. It is total BS i am sorry can put it in a polite way.

How is it total BS?

If a language is able to avoid bugs caused by uninitialized variables, it should.

Yes, and that is *exactly* why I've been saying the compiler should *track* whether or not something has been inited and then bitch when it's used without ebing inited, instead of just blindly tossing a '0' (or null, or NaN) in there with absolutely no knowledge whatsoever as to whether or not it actually *should* be starting out at '0' (or null, or NaN).
 But when it comes to a system language you have to preserve the rights of 
 those
 that know initialization is very expensive/or just plain unnecessary at 
 some point, and want compiler to ignore it.
 "something a = void;" is a very elegant solution, and it is explicit, you 
 have best of both worlds.

Uhh, yea, but what the hell does that have to do with what I said?
Nov 07 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:ib84bo$1vcn$1 digitalmars.com...
 "so" <so so.do> wrote in message news:op.vltk19pq7dtt59 so-pc...
 On Sun, 07 Nov 2010 22:41:41 +0200, Nick Sabalausky <a a.a> wrote:

 "so" <so so.do> wrote in message news:op.vls71ytk7dtt59 so-pc...
 On a related note, I *hate* that D silently sticks in a default value
 whenever anything isn't properly inited. This is one thing where I 
 really
 think C# got it right, and D got it wrong. And waving the "It's not
 leaving
 it with an undefined value like C does!" banner is an irritating
 strawman:
 Yea, it's better than C, but it still sucks.

This is one of the times (and this doesn't happen rarely) i am glad Walter is the head of D. It is total BS i am sorry can put it in a polite way.

How is it total BS?

If a language is able to avoid bugs caused by uninitialized variables, it should.

Yes, and that is *exactly* why I've been saying the compiler should *track* whether or not something has been inited and then bitch when it's used without ebing inited, instead of just blindly tossing a '0' (or null, or NaN) in there with absolutely no knowledge whatsoever as to whether or not it actually *should* be starting out at '0' (or null, or NaN).
 But when it comes to a system language you have to preserve the rights of 
 those
 that know initialization is very expensive/or just plain unnecessary at 
 some point, and want compiler to ignore it.
 "something a = void;" is a very elegant solution, and it is explicit, you 
 have best of both worlds.

Uhh, yea, but what the hell does that have to do with what I said?

I think I figured out what you meant. When I said "C# got it right", you thought I was talking about how C# doesn't allow any "int x = void;" whatsoever, right? That's not what I meant. I was talking about how C# issues a compile-time error whenever a variable is read before it's guaranteed to have been written (at least for local vars, I don't remember how it handles member vars).
Nov 07 2010
parent "Nick Sabalausky" <a a.a> writes:
"so" <so so.do> wrote in message news:op.vlua80b47dtt59 so-pc...
 I think I figured out what you meant. When I said "C# got it right", you
 thought I was talking about how C# doesn't allow any "int x = void;"
 whatsoever, right? That's not what I meant. I was talking about how C#
 issues a compile-time error whenever a variable is read before it's
 guaranteed to have been written (at least for local vars, I don't 
 remember
 how it handles member vars).

That too, and i am having hard time understanding the other as well, that i thought you mean "int x;" can cause bugs but not "int x=0;" and compiler should track "int x;"

Example: // Returns: Needs another pass? bool process(ref int a) {...} // Process arr in reverse order. // Certain elements may need more than one pass. void foo(int[] arr) { int i; // Oops! Meant "int i = arr.length;" while(i > 0) { bool shouldReprocess = process(arr[i]); if(!shouldReprocess) i--; } } Yes, there are probably other ways to write that, but the basic idea is that bugs can be caused by D's automatically assuming you wanted to initialize to a certain value. Requiring you to actually say what value you want to start with would give the programmer a chance to avoid these problems. Walter is worried that this would cause certain programmers to blindly toss in something like "= 0". I say, if that's a bad thing to do, then why in the world should the compiler AUTOMATE that exact same bad idea?
Nov 08 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Roman Ivanov <isroman.DEL ete.km.ru> wrote:

 I know what your mean, but the example is flawed:

 public void foo()
 {
     if (m) {
          Object p = new Object();
          p.toString();
     }
 }

You misunderstand. The idea is this: void foo( ) { Object p; if ( m ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // 20 lines of code here if ( m ) { p.doSomethingWeird( dataFromAbove ); } }
 int x = {
     if (s == Dri.one)
         return 1;
     else if (s == Dri.two)
         return 2;
     else
         return 3;
 }();

 This works. I'm not sure how efficient it is, but that would solve the
 issue with initialization without any path analysis.

I'm not sure the compiler actually does this, but it would be fairly trivial to inline that delegate. -- Simen
Nov 07 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Nick Sabalausky <a a.a> wrote:
 If you do that, then there's two possibilities:

 A. You intended p to get inited on all code paths but forgot a codepath.
 With real init-checking the compiler will tell you and you can fix it.  
 With
 D as it is, you're not informed at all, and you may or may not catch the
 problem before deployment. Obviously the former is better.

 B. You *intended* p to not always be inited, in which case the correct  
 code
 is this:

 void foo( ) {
   Object p=null;
   if ( m ) {
     p = new Object( );
     p.DoSomethingThatNeedsToBeDoneNow( );
   }
   // 20 lines of code here
   if ( p != null ) {
     p.doSomethingWeird( dataFromAbove );
   }
 }

There is a third option, wherein the if condition will only be true if p !is null, but the actual condition is more complex: if ( m > 4 ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // code if ( m > 12 ) { p.doSomethingWeird( dataFromAbove ); } -- Simen
Nov 07 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Nick Sabalausky <a a.a> wrote:

 - if ( m > 12 ) {
 + if ( p && m > 12 ) {

 And you can toss in an "if(m>12) assert(p);" if you're worried about  
 that.

Of course. But the point is, this is unnecessary. We know p !is null when m > 4. -- Simen
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
On Sun, 07 Nov 2010 22:41:41 +0200, Nick Sabalausky <a a.a> wrote:

 "so" <so so.do> wrote in message news:op.vls71ytk7dtt59 so-pc...
 On a related note, I *hate* that D silently sticks in a default value
 whenever anything isn't properly inited. This is one thing where I  
 really
 think C# got it right, and D got it wrong. And waving the "It's not
 leaving
 it with an undefined value like C does!" banner is an irritating
 strawman:
 Yea, it's better than C, but it still sucks.

This is one of the times (and this doesn't happen rarely) i am glad Walter is the head of D. It is total BS i am sorry can put it in a polite way.

How is it total BS?

If a language is able to avoid bugs caused by uninitialized variables, it should. But when it comes to a system language you have to preserve the rights of those that know initialization is very expensive/or just plain unnecessary at some point, and want compiler to ignore it. "something a = void;" is a very elegant solution, and it is explicit, you have best of both worlds. You could have just argued about that explicit initialization should be a must, that would make sense in another language but D way is much better. You have both explicit initialization and default initialization since you know "int i" is "int i=0" at the instant you are typeing, same goes for reader of your code. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Sun, 07 Nov 2010 14:09:01 -0800, Walter Bright wrote:

 Simen kjaeraas wrote:
 You misunderstand. The idea is this:
 
 void foo( ) {
   Object p;
   if ( m ) {
     p = new Object( );
     p.DoSomethingThatNeedsToBeDoneNow( );
   }
   // 20 lines of code here
   if ( m ) {
     p.doSomethingWeird( dataFromAbove );
   }
 }

You're right, the real cases where this kind of thing occurs are much more complex. I just posted the thing boiled down. And, of course, there's always a way to refactor the code to eliminate the spurious error message. But sometimes the result is as ugly as Pascal's efforts to prove you really don't need a 'break' statement in a loop. The real problem with the spurious errors is that then people will put in an initialization "just to shut the compiler up." Time passes, and the next guy is looking at the code and wonders why x is being initialized to a value that is apparently never used, or worse, is initialized to some bogus value randomly picked by the long-retired programmer. I've seen code reviewers losing a lot of time on this issue.

That's why we have immutable variables. They force you to think what to put in the variables. A lot of cases like the one above would be solved if if-then-else was an functional expression instead of a void returning statement. C/C++/D has the ternary ?: but the syntax is obfuscated. Object p = if (m) { ... foo; } else { ... bar; } instead of Object p; if (m) { ... p = foo; } else { ... p = bar; } There are even cases where the former can be const. The latter one has to be mutable in any case.
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
 That's why we have immutable variables. They force you to think what to
 put in the variables. A lot of cases like the one above would be solved
 if if-then-else was an functional expression instead of a void returning
 statement. C/C++/D has the ternary ?: but the syntax is obfuscated.

 Object p = if (m) {
   ...
   foo;
 } else {
   ...
   bar;
 }

 instead of

 Object p;
 if (m) {
   ...
   p = foo;
 } else {
   ...
   p = bar;
 }

 There are even cases where the former can be const. The latter one has to
 be mutable in any case.

Not related to this but i have to share. Try compiling this in C/C++. int i = i + 5; // something like this. This compiles with no warning, no error, nothing... I have absolutely no idea how this thing survived so long. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
 I think I figured out what you meant. When I said "C# got it right", you
 thought I was talking about how C# doesn't allow any "int x = void;"
 whatsoever, right? That's not what I meant. I was talking about how C#
 issues a compile-time error whenever a variable is read before it's
 guaranteed to have been written (at least for local vars, I don't  
 remember
 how it handles member vars).

That too, and i am having hard time understanding the other as well, that i thought you mean "int x;" can cause bugs but not "int x=0;" and compiler should track "int x;" -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
 int i;
 i = i + 5;

I can't see any uninitialized var there, can you? :P -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
On Mon, 08 Nov 2010 08:17:09 +0200, Jussi Jumppanen <jussij zeuseedit.com>  
wrote:

 so Wrote:

 Not related to this but i have to share.
 Try compiling this in C/C++.

 int i = i + 5; // something like this.

void main() { int i = i + 5; // something like this. } Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. test.c c:\temp\test.c(3) : warning C4700: uninitialized local variable 'i' used

I am using VC++ Express 2008, and last time i tried warning level was either 3 or 4, nothing. It is not a simple uninitialized variable bug, it is a serious error. At initialization rvalue should not contain anything about lvalue, this is absurd. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 07 2010
prev sibling next sibling parent so <so so.do> writes:
 There's no usage of an undeclared variable, but the right-hand-side of  
 the
 second line uses 'i' before *the programmer* initializes it. Yes, the D
 compiler chooses to automatically initialize it, but by doing so it  
 silently
 creates a bug every time the programmer intends 'i' to start out as  
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used  
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a  
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

So you want language force you to type either "int x=0;" or "int x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important. Again, for both coder and the code-reader there is no ambiguity here, every single thing is defined. -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Nov 08 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Daniel Gibson <metalcaedes gmail.com> wrote:

 Nick Sabalausky schrieb:
 "so" <so so.do> wrote in message news:op.vlv3iukp7dtt59 so-pc...
 There's no usage of an undeclared variable, but the right-hand-side  
 of the
 second line uses 'i' before *the programmer* initializes it. Yes, the  
 D
 compiler chooses to automatically initialize it, but by doing so it  
 silently
 creates a bug every time the programmer intends 'i' to start out as  
 anything
 other than 0. And it's not easily noticed since 0 is a commonly-used  
 value.
 (Something like 0xDEADBEEF would at least be an improvement (albeit a  
 small
 one) since at least that would stand out more and likely fail more
 spectacularly.)

x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important.

being *better* to automatically assume some init value baffles me.

It gives deterministic results/errors.

Yup. Also, as opposed to certain other solutions, it does not require advanced flow control, that is likely to be incomplete. Incomplete flow control here will make people write code 'to shut the compiler up'. And that is worse than uninitialized variables. -- Simen
Nov 09 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Nick Sabalausky <a a.a> wrote:

 Also, as I've said when this was discussed in the past, I wouldn't even  
 want
 the flow analysis to be perfect because all that would accomplish is to
 encourage garbage like the above (which would just end up being highly
 fragile anway).

But if it isn't, we must either define what it should do, or accept that some compilers will catch some of the uninitialized variable bugs, where others do not. We do not want that. -- Simen
Nov 09 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 07 November 2010 04:20:10 foobar wrote:
 Both the current D way and the C# way are ugly hacks.
 Ideally you should have TWO types: T and Option!T (ignore syntax for now).
 
 Most of the time you would use:
 auto variable = new T(params);  // no need for nulls here!
 and for the situations where you actually NEED to have a non-initialized
 variable you'd use: Option!T variable; // look ma, explicitly asking for a
 nullable variable
 
 Simple, ain't it? And it supports Walter's convoluted examples AND is
 explicit about it so it prevents illegal operations at compile time.

Making non-nullable the default would be a seriously breaking change and obviously contradict TDPL. So, I don't see that ever changing. Adding non- nullable support to the language could be done - it's likely to be an additive change and wouldn't break any code - but making them the default? Not going to happen. _Maybe_ if D3 ever materializes, it could happen then, but it would break a lot of code. - Jonathan M Davis
Nov 07 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, November 08, 2010 05:01:57 Simen kjaeraas wrote:
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 ??? Structs have no default constructor.

True. Disabling the default constructor here means that struct Foo{ disable this(); } Foo f; should halt compilation at Foo f;, and certain other places where a default constructor would normally be used.

I was not aware of that. I didn't think that you could do that for structs since default constructors are illegal in the first place. It would likely have the negative side effect of making it illegal to put Foo in arrays though, since it wouldn't be possible to fill in the array values with init.
 They have an init property with is used for default initialization.

Indeed. And this is different from a default constructor that you can't touch how?

Default initialization is used all over the place - including both stuff like the declaration Foo f; and in array declarations. Every type is default initialized when not initialized directly, and it has nothing to do with default constructors. init must be known at compile time whereas default constructors are run at runtime. Now, if disabling this() on a struct makes init unuseable, then it does become possible to disallow init, which is potentially very useful, but it could also become a problem with arrays and the like. Regardless, while default constructors and init may be similar for structs, since structs are value types and don't _have_ default constructors, they still aren't the same thing. init is essentially the value _before_ any constructor is called. You just can't have a default one.
 Either init needs to be changed to allow for real default constructors
 and/or objects need to be legal CTFE - ideally both.

Disabling the default constructor (which ostensibly looks this: this( ) { this = init; }) is a very good step in the right direction. Having proper default constructors would be better, and I can not remember the reason we don't have that.

It's because we need init, and init must be known at compile time. init gets used a fair bit (such as when you declare an array of a given size and the compiler _must_ initialize all of its elements), and it's vital to how D functions. However, D isn't currently able to insert any code which runs for any init values to default construct them. init must be entirely known at compile time. Depending on exactly how init works, it could be a _huge_ change to try and make a default constructor run instead. init makes a lot of sense as it stands for all types _except_ for structs, but it's definitely problematic for structs. - Jonathan M Davis
Nov 08 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, November 08, 2010 10:06:35 Walter Bright wrote:
 Jonathan M Davis wrote:
 I was not aware of that. I didn't think that you could do that for
 structs since default constructors are illegal in the first place. It
 would likely have the negative side effect of making it illegal to put
 Foo in arrays though, since it wouldn't be possible to fill in the array
 values with init.

That's right. The idea is to allow the declaration of a default constructor for structs, but only if it is marked as disabled. Then, any use of the struct that would require default initialization is disallowed. That includes static arrays, and arrays allocated via new. A dynamic array could be constructed using a literal or by appending the values one by one. This implies that a struct containing a field that has a disabled default constructor also cannot be default initialized. Of course, you can defeat these protections by using a cast and encapsulating the code that does that. I believe Andrei was thinking of this when he mentioned using a template to initialize a nonnull array.

How hard would it really be to insert code wherever a struct's init value is used to default construct it either when the program starts up (for globals) or right after it's declared (for locals)? If you could do that, then I don't see why we couldn't have proper default constructors. All I can assume is that adding that extra code to default construct structs would be painful change to make to dmd, but perhaps there's something else that I'm missing. If that could be done though, it would be a _huge_ improvement. - Jonathan M Davis
Nov 08 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 I was not aware of that. I didn't think that you could do that for  
 structs since
 default constructors are illegal in the first place.

And you can't. That doesn't mean it can't be possible in the future.
 It would likely have the
 negative side effect of making it illegal to put Foo in arrays though,  
 since it
 wouldn't be possible to fill in the array values with init.

Indeed. That's been discussed here, and in short, these are the rules: Arrays of non-null cannot have their lengths increased. To create an array of non-null, use a literal, a function that checks all parameters and builds an array from it, or a function that checks all elements of a passed array of nullable elements. To create a longer array, append two arrays.
 They have an init property with is used for default initialization.

Indeed. And this is different from a default constructor that you can't touch how?

Default initialization is used all over the place - including both stuff like the declaration Foo f; and in array declarations. Every type is default initialized when not initialized directly, and it has nothing to do with default constructors. init must be known at compile time whereas default constructors are run at runtime.

I mean, the compiler is doing something to move .init to the new instance of the type, and this could very well be considered the default constructor.
 init gets used a fair bit (such as when you declare an array of a given
 size and the compiler _must_ initialize all of its elements), and it's
 vital to how D functions.

static arrays would require initializers in this case: NonNull!(T)[2]; // Compile-time error: NonNull!T has no default constructor NonNull!(T)[2] = [nonNull(new T), nonNull(new T)]; // Works
 Depending on exactly how init works, it could be a _huge_ change to try  
 and make a default constructor run instead.

Possibly, though I don't think so. As said before, some code needs to be executed to copy .init to the new instance. This code could likely be replaced with a default constructor. Even so, the disabling of default constructors is not really about constructors, but as you say, about disabling .init. This will not require execution of new code, only a compile-time check for whether the constructor has been disabled. -- Simen
Nov 08 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 08 November 2010 20:10:42 Walter Bright wrote:
 Jonathan M Davis wrote:
 Well, if it's a default constructer which has to be nothrow and maybe
 pure or nothing, I'll take the default constructor. Now, if there's a
 better way to handle this which would result in full-blown, arbitrary
 default constructors, then that would be better, but something is better
 than nothing.

I just hate to get into the complex, buggy quagmire C++ has with this.

We definitely want to avoid the nastier issues, but the lack of default constructors continually pops up as being a major problem. The QtD folks in particular were have a lot of problems because of it as I understand. I've certainly had problems with it my code. Even a restricted default constructor would be better than none.
 Another thought relates to the discussion of  disable on a struct's
 default constructor to disable init. What if we allowed default
 constructors but disabled init in the same way that we'd be doing with
  disable? So, a struct uses init if it has no default constructor, but
 if it has one, then you can't use it where init would be required. Would
 that help fix things? It certainly would make some sense given that when
 using a default constructor, you don't really want init anyway. It could
 just be annoying with static arrays and the like (though = void should
 make it possible to make that work I think, if you really need to).

Actually, you do as init takes care of most of the initializations, and ensures the fields have predictable values before the ctor starts.

Okay. Yes init is the state prior to the constructor being run, which is fine - you have the same with classes except that there is no init that you can directly access. The problem is when you can have init _without_ calling a constructor. If we could have default constructors on structs but have it so that any struct with a default constructor cannot be used where you have to have a bare init with no constructor call, then that at least seems like it would mostly solve the problem. In the few places where it's a problem - such as array construction - some kind of library solution could probably solve it (for dynamic arrays at least). - Jonathan M Davis
Nov 08 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Jonathan M Davis <jmdavisProg gmx.com> wrote:
 We definitely want to avoid the nastier issues, but the lack of default
 constructors continually pops up as being a major problem. The QtD folks  
 in
 particular were have a lot of problems because of it as I understand.  
 I've
 certainly had problems with it my code. Even a restricted default  
 constructor
 would be better than none.

In my experience, disabling the default constructor would be enough. That way the compiler warns you that 'this object has no valid default state, so use a constructor.'
 If we could have default constructors on structs but have it so that any  
 struct
 with a default constructor cannot be used where you have to have a bare  
 init
 with no constructor call

And where is that? Currently no such thing exists, as we have no default constructors for structs. That means we could interpret any use of .init as a constructor call. Disabling default constructors has this same advantage, while avoiding the problems. The only problem is you will have to use a constructor explicitly. -- Simen
Nov 09 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday 09 November 2010 02:22:31 Simen kjaeraas wrote:
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 We definitely want to avoid the nastier issues, but the lack of default
 constructors continually pops up as being a major problem. The QtD folks
 in
 particular were have a lot of problems because of it as I understand.
 I've
 certainly had problems with it my code. Even a restricted default
 constructor
 would be better than none.

In my experience, disabling the default constructor would be enough. That way the compiler warns you that 'this object has no valid default state, so use a constructor.'

Except that I _want_ to have default constructors for structs. Sure, having a struct which can't be used without constructing it (i.e. just using init) and which has no default constructor can be useful, but having default constructors would be _huge_.
 If we could have default constructors on structs but have it so that any
 struct
 with a default constructor cannot be used where you have to have a bare
 init
 with no constructor call

And where is that? Currently no such thing exists, as we have no default constructors for structs. That means we could interpret any use of .init as a constructor call. Disabling default constructors has this same advantage, while avoiding the problems. The only problem is you will have to use a constructor explicitly.

There has been discussion in the nullable reference thread of making disable this() {} for structs disable init so that you can only use such structs by constructing them, and any place that would use init (such as a static array) would not be legal for such a struct. I'm suggesting that we do something similar to that except that we make it so that having a default constructor disables init and constructing the struct with the default constructor is then one of the legal ones to construct the struct. - Jonathan M Davis
Nov 09 2010