January 02, 2023
On Sunday, 1 January 2023 at 18:18:57 UTC, Walter Bright wrote:
> On 12/31/2022 7:06 PM, Timon Gehr wrote:
>> No, it absolutely, positively does not... It only ensures no null dereference takes place on each specific run. You can have screwed it up and only notice once the program is published. I know this happens because I have been a _user_ of software with this kind of problem. Notably this kind of thing happens in released versions of DMD sometimes...
>
> You're absolutely right. And if I do a pattern match to create a non-nullable pointer, where the null arm does a fatal error if it can't deal with the null, it's the same thing.

That's a logically flawed argument because it rests on the assumption that the only possible way to handle a null when converting from a nullable to a non-nullable is to abort the program.

It's also a strawman because it also misses the point that its not about what you do with the null, but knowing where they might get in. Statically knowing where the external doors are.


>> That's great. However, it's somewhat aggravating to me that I am currently not actually convinced you understand what's needed to achieve that. This is because you are making statements that equate nonnull pointers in the type system to runtime hardware checking with segmentation faults.
>
> Yes, I am doing just that.

It's not the same because non-nullables allow you to narrow down the places where the abort can happen. It's like putting metal detectors on doorways. When someone passes through a metal detector they get a id badge, with that badge they can pass through any doorway. If there are only 3 external entrances you put the metal detectors on those. Now the building is safe. The nullable to non-nullable conversions are the metal detectors.
January 02, 2023
On 12/31/2022 2:28 AM, Max Samukha wrote:
> For types that require runtime construction, initializing to T.init does not result in a constructed object.
The idea is to:

1. have construction that cannot fail. This helps avoid things like double-fault exceptions

2. have initializers that can be placed in read only memory

3. have something to set a destroyed object to, in case of dangling references and other bugs

4. present to a constructor an already initialized object. This prevents the common C++ problem of adding a field and forgetting to construct it in one of the overloaded constructors, a problem that has plagued me with erratic behavior

5. provide a NaN state. I know many people don't like NaN states, but if one does, the default construction is perfect for implementing one.

6. it fits in well with (future) sumtypes, where the default initializer can be the error state.

An alternative to factory functions is to have a constructor with a dummy argument. Nothing says one has to actually use the parameters to a constructor.
January 02, 2023
On 1/1/2023 11:14 AM, kdevel wrote:
> On Sunday, 1 January 2023 at 18:11:10 UTC, Walter Bright wrote:
>> (C++ has the same issue.)
> 
> How? Quote from https://eel.is/c++draft/dcl.ref:
> 
>     [...] A reference shall be initialized to refer to a valid object or function.
> 
>     [Note 2: In particular, a null reference cannot exist in a well-defined
>     program, because the only way to create such a reference would be to bind it
>     to the “object” obtained by indirection through a null pointer, which causes
>     undefined behavior. {...} — end note]

The spec says don't do it. That doesn't stop it from happening, though, as the compiler has no way to detect it.

January 03, 2023
On Monday, 2 January 2023 at 22:54:41 UTC, Walter Bright wrote:
> On 1/1/2023 11:14 AM, kdevel wrote:
>> On Sunday, 1 January 2023 at 18:11:10 UTC, Walter Bright wrote:
>>> (C++ has the same issue.)
>> 
>> How? Quote from https://eel.is/c++draft/dcl.ref:
>> 
>>     [...] A reference shall be initialized to refer to a valid object or function.
>> 
>>     [Note 2: In particular, a null reference cannot exist in a well-defined
>>     program, because the only way to create such a reference would be to bind it
>>     to the “object” obtained by indirection through a null pointer, which causes
>>     undefined behavior. {...} — end note]
>
> The spec says don't do it.

The C++ standard does not primarily say "don't do this or that" but "if you do this or that your code does not form a valid program".

> That doesn't stop it from happening,

By definition it does not happen in valid C++ programs. Urge programmers to write valid C++ programs and all's right with the world!

> though, as the compiler has no way to detect it.

It's like speeding. The guys who deploy road signs are not in charge of surveilling the traffic.
January 03, 2023
On 1/1/2023 7:32 AM, Richard (Rikki) Andrew Cattermole wrote:
> Only issue is when it doesn't give you a stack trace.

That could probably be added. After all, we already give a stack trace for some other kinds of errors.

January 03, 2023
On 1/2/2023 3:20 AM, mate wrote:
> I don’t understand how it can be argued that both approaches are equivalent. In the hardware case, the null check is done at runtime. If the programmer forgets to handle some edge case, the bug gets released and affects the customers in production. In the type system case, the language ensures that, when the appropriate type is used, this bug cannot happen. In other words, that fixes the billion dollar mistake.


May I quote myself:

"if I do a pattern match to create a non-nullable pointer, where the null arm does a fatal error if it can't deal with the null, it's the same thing."
January 03, 2023
On 1/2/2023 1:50 PM, claptrap wrote:
> That's a logically flawed argument because it rests on the assumption that the only possible way to handle a null when converting from a nullable to a non-nullable is to abort the program.

I never said "only possible way". However, it is the usual case. Now, if the *user* checks a pointer for null, and it isn't, then it won't seg fault from subsequent use.


> It's also a strawman because it also misses the point that its not about what you do with the null, but knowing where they might get in. Statically knowing where the external doors are.

Indeed, that is a difference. I've rarely had any actual trouble tracking it back to its source, though.

January 03, 2023
On Wednesday, 21 December 2022 at 19:09:37 UTC, Walter Bright wrote:
> https://news.ycombinator.com/edit?id=34084894
>
> I'm wondering. Should I just go ahead and implement [..] in ImportC?

I think you should do it, you're losing much more time arguing about it's value than if you did it from the start
People want you to work on this and that but argue so much with you on a non-breaking addition that you don't have the time to work at all

As far as I understand it's nothing more than a syntax for D slices into betterC, which mean most of the code is already written (maybe it could use D slice syntax directly for a better transition from betterC to full D if wanted?)
January 03, 2023
On Monday, 2 January 2023 at 22:53:30 UTC, Walter Bright wrote:
> On 12/31/2022 2:28 AM, Max Samukha wrote:
>> For types that require runtime construction, initializing to T.init does not result in a constructed object.
> The idea is to:
>
> 1. have construction that cannot fail. This helps avoid things like double-fault exceptions

I understand the idea. My point (again) is that an object initialized to a dummy value is not a constructed object. Yes, the dummy value is better than random garbage. However, you cannot say "construction cannot fail". An object initialized to a dummy value has not been constructed. You have deferred construction to a later point, and it may fail there. Lazy initialization, initialization in factory functions, etc. is nothing but deferred construction.

Please take a look at C#. They rightfully distinguish `default(T)` (their equivalent of `T.init`) from `T()`. From https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#struct-initialization-and-default-values:
"That creates a distinction between an uninitialized struct, which has its default value and an initialized struct, which stores values set by constructing it."

See, they don't pretend that "set to default value" is "initialized"?

>
> 2. have initializers that can be placed in read only memory

That seems to be irrelevant to my argument. Fully constructed objects can be serialized to ROM as well.

>
> 3. have something to set a destroyed object to, in case of dangling references and other bugs

Yes, the object is set to the dummy state before construction and after destruction. Constructors are for construction. `this()` is a constructor. Just unban it.

>
> 4. present to a constructor an already initialized object. This prevents the common C++ problem of adding a field and forgetting to construct it in one of the overloaded constructors, a problem that has plagued me with erratic behavior

`this()` is a constructor - present to it the default-initialized object just as you do to other constructors.

>
> 5. provide a NaN state. I know many people don't like NaN states, but if one does, the default construction is perfect for implementing one.

That is irrelevant to my point. I am not arguing against default initialization. Just don't call it construction. Constructors replace the dummy value with a useful one.

>
> 6. it fits in well with (future) sumtypes, where the default initializer can be the error state.

No objection.

>
> An alternative to factory functions is to have a constructor with a dummy argument. Nothing says one has to actually use the parameters to a constructor.

I know about the dummy argument hack. I just see no reason why I have to do that.
January 03, 2023
On Tuesday, 3 January 2023 at 08:49:55 UTC, Walter Bright wrote:
> On 1/2/2023 1:50 PM, claptrap wrote:
>> That's a logically flawed argument because it rests on the assumption that the only possible way to handle a null when converting from a nullable to a non-nullable is to abort the program.
>
> I never said "only possible way". However, it is the usual case. Now, if the *user* checks a pointer for null, and it isn't, then it won't seg fault from subsequent use.

It's implicit in your position. If you want to say "they are the same" then you have to also hold that they must always abort on null. "Usually" isnt enough

A usually aborts on NULL
B always aborts on NULL
Therefore they are the same...

***doesn't work***.


>> It's also a strawman because it also misses the point that its not about what you do with the null, but knowing where they might get in. Statically knowing where the external doors are.
>
> Indeed, that is a difference. I've rarely had any actual trouble tracking it back to its source, though.

It'd be like you could verify 90% of your numeric code wont generate NaNs at compile time, and so you'd only have to check the boundry between the 10% that can and the 90% that cant.

It'd be more useful than default to NaN is with floats. And you like that?