September 27, 2009
Sun, 27 Sep 2009 00:08:50 -0400, Jeremie Pelletier thusly wrote:

> Ary Borenszweig wrote:
>> Just out of curiosity: have you ever programmed in Java or C#?
> 
> Nope, never got interested in these to tell the truth. I only did C, C++, D and x86 assembly in systems programming, I have quite a background in PHP and JavaScript also.

So you only know imperative procedural programming + some features of hybrid OOP languages that are not even proper OOP languages.

> 
> I played with a lot of languages, but those are the ones I use on a daily basis. I would like to get into Python or Ruby someday, I only hear good things about these two. I know LUA has less overhead than Python

Oh, the only difference between LUA and Python is the overhead?! That's a... pretty performance oriented view on languages.

> I like extremes :)

If you like extremes, why have you not programming in Haskell or Coq? Too scary? You are often arguing against languages and concepts you have never used. The other people here who make these suggestions are more experienced with various languages.
September 27, 2009
Denis Koroskin wrote:
> On Sun, 27 Sep 2009 03:01:48 +0400, Walter Bright <newshound1@digitalmars.com> wrote:
> 
>> Denis Koroskin wrote:
>>> One more:
>>>  T foo(bool someCondition)
>>> {
>>>     T? t;
>>>     if (someCondition) t = someInitializer();
>>>     // ...
>>>      if (t.isNull) { // not initialized yet
>>>         // ...
>>>     }
>>>      return enforce(t); // throws if t is not initialized yet,
>>> because foo *must* return a valid value by a contract
>>> }
>>
>> It seems to me you've got null references there anyway?
>>
>> What would you do about:
>>
>>     T[] a;
>>     a[i] = foo();
>>
>> where you want to have unused slots be null (or empty, or nothing)?
> 
> Easy:
> 
> T? foo(); // returns valid object or a null
> 
> T?[] a;
> a[i] = foo();

The case of a non-null array is, I think, worthy of some more consideration.

These are the things that would not be possible with a non-nullable array:

- newing it
- setting .length to a greater value
- appending a nullable array of the same base type.

Basically, anything that may fill it with nulls.

The only two allowed instructions would be ~= NonNullable and ~= NonNullableArray. And it's good that way.
September 27, 2009
Jeremie Pelletier wrote:
> Jarrett Billingsley wrote:
>> On Sat, Sep 26, 2009 at 11:23 PM, Jeremie Pelletier <jeremiep@gmail.com> wrote:
>>
>>> There is no such thing as "not being able to happen" :)
>>>
>>> Object thisCannotPossiblyBeNullInAnyWayWhatsoever = cast(Object)null;
>>>
>>> I seem to be the only one who sees Walter's side of things in this
>>> thread
>>> :o)
>>
>> Why the hell would the compiler allow that to begin with? Why bother implementing nonnull references only to allow the entire system to be broken?
> 
> Because D is a practical language that let the programmer do whatever he wants, even shoot his own foot if he wants to. Doing so just isn't as implicit as in C.
> 
> Walter understands there are some cases where you want to override the type system, that's why casts are in D, too many optimizations rely on it.

Sure, but if you set out to break it the compiler really can't (or shouldn't) help you. This whole debate, as far as I know, is about defaults, i.e. preventing *unintentional* nulls.
September 27, 2009
Jeremie Pelletier wrote:
> Christopher Wright wrote:
>> Jeremie Pelletier wrote:
>>> What if using 'Object obj;' raises a warning "unitialized variable" and makes everyone wanting non-null references happy, and 'Object obj = null;' raises no warning and makes everyone wanting to keep the current system (all two of us!) happy.
>>>
>>> I believe it's a fair compromise.
>>
>> It's a large improvement, but only for local variables. If your segfault has to do with a local variable, unless your function is monstrously large, it should be easy to fix, without changing the type system.
>>
>> The larger use case is when you have an aggregate member that cannot be null. This can be solved via contracts, but they are tedious to write and ubiquitous.
> 
> But how would you enforce a nonnull type over an aggregate in the first place? If you can, you could also apply the same initializer semantics I suggested earlier.
> 
> Look at this for example:
> 
> struct A {
>     Object cannotBeNull;
> }
> 
> void main() {
>     A* a = new A;
> }
> 
> Memory gets initialized to zero, and you have a broken non-null type. You could have the compiler throw an error here, but the compiler cannot possibly know about all data creation methods such as malloc, calloc or any other external allocator.
> 
> You could even do something like:
> 
> Object* foo = calloc(Object.sizeof);
> 
> and the compiler would let you dereference foo resulting in yet another broken nonnull variable.
> 
> Non-nulls are a cute idea when you have a type system that is much stricter than D's, but there are just way too many workarounds to make it crash in D.

"Here are some cases you haven't mentioned yet. This proves that the compiler can't possibly be smart enough. "

Yeeeeeah.

In the above case, why not implicitly put the cannotBeNull check into the struct invariant? That's where it belongs, imho.

Regarding your example, it's calloc(size_t.sizeof). And a) we probably can't catch that case except with in/out null checks on every method, but then again, how often have you done that? I don't think it's relevant enough to be relevant to this thread. :)
September 27, 2009
Sun, 27 Sep 2009 00:27:14 -0700, Walter Bright thusly wrote:

>> You seem to be under the impression that nothing can be made uncrashable without introducing the possibility of corrupted state. That's hogwash.

What I mean by safe is that no matter what you do, you cannot make the program crash or cause memory corruption. If you look at typical functional languages, unless FFI is used, the only ways the program may fail are a) no more stack memory b) no more heap memory c) programs halts (halting problem) d) developer explicitly kills the program e.g. with the Error type. Note that if your language is simple enough, say simply typed lambda calculus, you do not have the third problem anymore. All of these errors can also happen in D, but none of the D's other problems happen in those languages.
September 27, 2009
Walter Bright wrote:
> Andrei Alexandrescu wrote:
>> Walter Bright wrote:
>>> Even forcing an explicit initializer doesn't actually solve the problem - my experience with such features is programmers simply insert any old value to get the code to pass the compiler, even programmers who know it's a bad idea do it anyway.
>>
>> I think you're starting to be wrong at the point where you don't realize that many bugs come from references that people have forgotten to initialize. Once you acknowledge those, you will start to realize that a reference that must compulsively be initialized is valuable.
> 
> The problem is it's worse to force people to provide an initializer. 

You aren't forcing them. They decide for themselves. They determine whether it's appropriate for a particular variable to be null.

You can achieve the same goal through contracts. However, this is much more verbose -- enough so that you'll only add these contracts when hunting down a bug. And if you have an array of things

> It isn't a theoretical problem with providing bad initializers just to shut the compiler up. I have seen it in the wild every time some manager required that code compile without warnings and the compiler warned about no initializer.

C# requires that every variable be initialized before use. You know how often I get such an error? Maybe once for every 100 hours of coding. It's mainly for cases where I expect an integer to be initialized to 0 and it's not. You know how often I provide a bad initializer to shut the compiler up? Never.

This is partially because C#'s compiler has good flow analysis. It's mostly because:
 - I declare variables where I use them, not beforehand.
 - I often declare variables via IDE commands -- I write the code to fetch or calculate a value and assign it to a variable that doesn't exist, and the IDE fills in the type and declares it in the correct place.
 - I usually don't have more than four or five local variables in a function (often no more than one or two). Out of 300KLOC, there are a few dozen functions that break this rule.

DMDFE functions are often long, complex, and have many local variables. I see how this would conflict with your coding style. You would have to add a few question marks for each function, and then you'd be done. DMDFE is ~60KLOC, but you could probably switch it over to this type system without structural changes to any function in a couple days.
September 27, 2009
Jeremie Pelletier wrote:
> There again, I favor stronger initialization semantics over nonnull types. This will get rid of most of these errors

Only for local variables. Not for fields.

> Most segfaults I have take me at most a few minutes to pinpoint. Its finding backdoors to compiler enforcements thats annoying.

You're complaining now because you'd try to cram 'null' down the throat of something marked 'not-null' and fear it would be difficult?
September 27, 2009
Michel Fortin wrote:
> On 2009-09-26 22:07:00 -0400, Walter Bright <newshound1@digitalmars.com> said:
> 
>> [...] The facilities in D enable one to construct a non-nullable type, and they are appropriate for many designs. I just don't see them as a replacement for *all* reference types.
> 
> As far as I understand this thread, no one here is arguing that non-nullable references/pointers should replace *all* reference/pointer types. The argument made is that non-nullable should be the default and nullable can be specified explicitly any time you need it.
> 
> So if you need a reference you use "Object" as the type, and if you want that reference to be nullable you write "Object?". The static analysis can then assert that your code properly check for null prior dereferencing a nullable type and issues a compilation error if not.

I dislike these forced checks.

Let's say you're dealing with a compiler frontend. You have a semantic node that just went through some semantic pass and is guaranteed, by flow control and contracts, to have a certain property initialized that was not initialized prior to that point.

The programmer knows the value isn't null. The compiler shouldn't force checks. At most, it should have automated checks that disappear with -release.

Also, it introduces more nesting.

Also, unless the compiler's flow analysis is great, it's a nuisance -- you can see that the error is bogus and have to insert extra checks.

It should be fine to provide a requireNotNull template and leave it at that.
September 27, 2009
Walter Bright wrote:

> Name any single part or system on a Boeing airliner, and if it vanishes abruptly in a puff of smoke, the airliner will survive it.

Except this sentence I applaud every thought.

If "single part" includes the passenger area, the meaning of this sentence is upright ridiculous.

-manfred
September 27, 2009
On 2009-09-27 07:38:59 -0400, Christopher Wright <dhasenan@gmail.com> said:

> I dislike these forced checks.
> 
> Let's say you're dealing with a compiler frontend. You have a semantic node that just went through some semantic pass and is guaranteed, by flow control and contracts, to have a certain property initialized that was not initialized prior to that point.
> 
> The programmer knows the value isn't null. The compiler shouldn't force checks. At most, it should have automated checks that disappear with -release.

If the programmer knows a value isn't null, why not put the value in a nullable-reference in the first place?


> Also, it introduces more nesting.

Yes and no. It introduces an "if" statement for null checking, but only for nullable references. If you know your reference can't be null it should be non-nullable, and then you don't need to check.


> Also, unless the compiler's flow analysis is great, it's a nuisance -- you can see that the error is bogus and have to insert extra checks.

First you're right, if the feature is implemented it should be well implemented. Second, if in a few place you don't want an "if" clause, you can always cast your nullable reference to a non-nullable one, explicitly bypassing the safeties. If you write a cast, you are making a consious decision of not checking for null, which is much better than the current situation where it's very easy to forget to check for null.


> It should be fine to provide a requireNotNull template and leave it at that.

It's fine to have such a template. But it's not nearly as useful.


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19