On Sunday, 1 January 2023 at 01:58:18 UTC, Walter Bright wrote:
>On 12/30/2022 11:55 PM, Sebastiaan Koppe wrote:
>In a larger program the first one allows the programmer to do the check once and rely on it for the remainder of the program.
Which is what usually happens with nullable pointers. We check once and rely on it to be non-null for the rest of the program
Oh but here is the difference, with nonnull pointers its the type system that keeps me honest and in your case its another responsibility for the programmer.
> >Essentially it leverages the type system to make invalid state unrepresentable.
I actually do understand that, I really do. I'm pointing out that the hardware makes dereferencing null pointers impossible. Different approach, but with the same end result.
Valuable software is forever in a state of change; a series of additions, refactorings, removals etc. To make those changes easier it helps if you limit the assumptions each line of code has. Things like: "This dereference on line 293 is fine because we checked it at line 237" make refactorings harder. In order to keep high plasticity you want to limit these implicit - invisible - connections.
Having unittests helps fearless refactoring. Using the type system to your advantage helps a lot too.
> >This simplifies subsequent code.
I'm not so sure it does. It requires two types rather than one - one with the possibility of a null, one without. Even the pattern matching to convert the type is more work than:
if (p) ...
Yes, it is a little bit more work, but that often happens somewhere on the boundary of the (sub)program, so that the remainder can just work with the more restricted type.
Also, somewhat tangentially related, isn't nonnull-ness one of the things ref
helps with:
// transmogrifies `p`, `p` must be non-null!
void transmogrify1(int* p);
// transmogrifies `p`
void transmogrify2(ref int p);
transmogrify2
uses the type system to express its non-null requirement, which anyone can see just glancing at the signature. That is worth something.
Personally, I'm most interested in sumtypes and pattern matching as a better error handling mechanism than throwing exceptions.
Very happy to hear that. I recently wrote https://github.com/skoppe/oras/blob/master/source/oras/client.d which uses mir's algebraics to make all the possible errors explicit. On top of that it uses nothrow
to make sure no exceptions slip in due to changes.