February 04
On Sunday, 4 February 2024 at 12:35:02 UTC, Jonathan M Davis wrote:

I get it, you are interested in the propagation mechanic, it can be done with error handling code too, take a look at Swift/Zig, take a look at Go2's proposal, many have succesfully moved away from EH, I learnt from them

There is no reason to be stubborn about the way it is currently done in phobos, DMD was successfully built without exceptions, let's embrace and enhance this

My game+engine are also successfully built without EH, and i'd love to move away from the C way of checking errors, and i'm envious of how other people are doing it with their other languages
February 04

On Friday, 2 February 2024 at 18:39:33 UTC, Hipreme wrote:

>

On Friday, 2 February 2024 at 18:19:47 UTC, jmh530 wrote:

>

On Friday, 2 February 2024 at 09:09:37 UTC, Adam Wilson wrote:

>

Walter and I had a productive conversation yesterday about Phobos 3 and we felt it would be appropriate to share some notes on our discussion.

[snip]

When it comes to discussions around functions that allocate to the heap, I think there is some benefit in implementing lower-level functionality in functions that do not do any allocations whatsoever. You can then have higher level API above that that controls allocation and is more convenient to use. That way users can opt in to whatever they want or write their own more convenient API on top of the lower level functions.

May not work for everything, but could be a useful approach.

It may be still a bad solution. Countless times I have done functions which know how much to preallocate. The eager solution always seems to be a lot faster, and I have noticed the pattern of using map or filter or anything more + .array looking to infinitely slower than if you had a direct version. And I don't know about release versions. The thing is that 99% of the time I'm testing on debug builds and getting less speed only because I'm on debug looks really bad.

The work I’ve been doing is related to linear algebra. Nothing really lazy or map-like. For instance, think of it like dgemm doing matrix multiplication without doing any allocations. It takes a pointer to the result matrix as an input and requires information about how if that will be. A higher level D version could work in terms of slices so that the API is cleaner. A higher level API could handle the case where the result is allocated with GC.

February 04
On Sunday, 4 February 2024 at 12:35:02 UTC, Jonathan M Davis wrote:
> On Sunday, February 4, 2024 3:13:39 AM MST ryuukk_ via Digitalmars-d wrote:
>> EH is evil, some platforms has banned it completly (apple), and microsoft is all in rust, they are rewritting their C# application in Rust too, not just C++, it is an inferior design
>>
>> https://www.theregister.com/2024/01/31/microsoft_seeks_rust_developers/
>
> A number of us do not agree with you, and I for one never will. For many situations, exceptions are by far the best error handling mechanism that there is.

I personally find exceptions useful only when I want to jump many stack frames at once.

Overall I find them taxing because they add another possible 'return' value to a function. Sometimes its documented, but 9 out of 10 times you'll find out at runtime.

Knowing all the possible error states of a function helps me enormously in avoiding surprises later on. While it might seem overwhelming at first, I find it liberating because the complete state space is right in front of me. I guess it is similar to the benefits of local reasoning.

Note I often write server programs. YMMV with e.g. batch programs.
February 04
On Sunday, February 4, 2024 8:04:00 AM MST Paul Backus via Digitalmars-d wrote:
> On Sunday, 4 February 2024 at 09:30:58 UTC, Jonathan M Davis
>
> wrote:
> > The main thing that it needs IMHO is going through it and making sure that it's doing the right thing for each trait and that the exact set of traits that it has is really what we want, since right now, several of them are doing the wrong thing (e.g. treating enum as if it were its base type with stuff like isIntegral, isNumeric, etc., which is a source of bugs).
>
> I have bad news for you. This behavior is not the fault of std.traits, but is actually baked into the language itself. For
>
> example, the spec for __traits(isIntegral) says:
> > The integral types are: byte, ubyte, short, ushort, int, uint, long, ulong, cent, ucent, bool, char, wchar, dchar, vectors of integral types, and enums with an integral base type.
>
> https://dlang.org/spec/traits.html#isIntegral
>
> Maybe we can clean this up in a new language edition, but as long as this is how the underlying __traits work, std.traits should work the same way.

std.traits doesn't actually rely on that. It does its own checks for enum, and it does so inconsistently (e.g. isSomeString doesn't allow enums, and it was a source of bugs and frustration when it did years ago, because it meant that range-based code was accepting enums with a base type of string and then not working, because they aren't actually ranges). Traits and template constraints in general need to be checking for either an exact list of types or for whether the provided type has a particular set of capabilities. Allowing implicit conversions is almost always a source of bugs. So, while it should obviously be possible to check for implicit conversions where appropriate, IMHO, it's a big mistake for Phobos to encourage it or make it the easy the default. It should not be necessary to put !is(T == enum) all over the place in code to stop enums or alias this from making it into a function and then not behaving properly, because the type wasn't actually converted, and the fact that std.traits is not principled about that is one of its biggest problems IMHO - though as with a number of things that need to be fixed in Phobos, they're mistakes that really only become clear after more experience with what we attempted the first time around.

So, regardless of what __traits is doing, I will argue quite strongly that std.traits should not be treating anything as its base type unless that's truly what makes sense for that particular trait, and if __traits isn't doing that, then it should probably fixed in a future edition as well. But ultimately, __traits is a building block for other traits, so what std.traits does does not have to be the same thing, though having the same name in __traits and std.traits do something different from each other is something that should probably be avoided. So, I agree that having __traits(isIntegral, T) accept enums and std.traits.isIntegral reject them is not a good idea. But my solution at that point is to not call the trait in std.traits isIntegral but rather given a distinct name.

- Jonathan M Davis



February 04
On Sunday, 4 February 2024 at 15:54:53 UTC, ryuukk_ wrote:
> On Sunday, 4 February 2024 at 12:35:02 UTC, Jonathan M Davis i'd love to move away from the C way of checking errors, and i'm envious of how other people are doing it with their other languages

Why to be envious, and not just use this one for example https://tchaloupka.github.io/expected/expected.html ?
February 04
On Sunday, 4 February 2024 at 15:54:53 UTC, ryuukk_ wrote:
> On Sunday, 4 February 2024 at 12:35:02 UTC, Jonathan M Davis wrote:
>
> I get it, you are interested in the propagation mechanic, it can be done with error handling code too, take a look at Swift/Zig, take a look at Go2's proposal, many have succesfully moved away from EH, I learnt from them
>
> There is no reason to be stubborn about the way it is currently done in phobos, DMD was successfully built without exceptions, let's embrace and enhance this

No a fun of checking errors by return. Such code looks more difficult to read than it should. So it possible but not ideal. These language have gotten very little use out there. Exception is proven and works.

>
> My game+engine are also successfully built without EH, and i'd love to move away from the C way of checking errors, and i'm envious of how other people are doing it with their other languages


February 04
On Sun, Feb 04, 2024 at 09:10:20PM +0000, Sebastiaan Koppe via Digitalmars-d wrote: [...]
> I personally find exceptions useful only when I want to jump many stack frames at once.

But I thought exceptions were not intended to be used in that kind of way. They're more like an abort condition to short-circuit the normal flow of control. If a failure condition is an expected part of the program, throwing an exception is probably not the best way of implementing it.


> Overall I find them taxing because they add another possible 'return' value to a function. Sometimes its documented, but 9 out of 10 times you'll find out at runtime.

But isn't it enough to just have a blanket catch block in the dispatch code to swallow any exceptions that downstream code might generate? You don't have to know exactly what it is, you just catch it, abort the offending task, and move on to the next one?


> Knowing all the possible error states of a function helps me enormously in avoiding surprises later on. While it might seem overwhelming at first, I find it liberating because the complete state space is right in front of me.  I guess it is similar to the benefits of local reasoning.
> 
> Note I often write server programs. YMMV with e.g. batch programs.

There are certainly valid considerations about the predictability of control flow. I.e., if you call a series of functions in a row and they are all nothrow, you can rest assured that the control flow will reach the end of the block, and not bail out on you in the middle.

Still, when a fatal error happens deep inside some low-level code and there's nothing else you can do until you get back to the high-level business logic, exceptions are very useful. To manually bubble the error state all the way back up the call stack is just too onerous. It forces all intermediate levels of code to be dependent on the exact error codes / sumtypes so that it can be properly propagated up to the level where the code has enough context to react to it meaningfully. This increases coupling between orthogonal code. Using exceptions avoids this spurious coupling.


T

-- 
People tell me I'm stubborn, but I refuse to accept it!
February 05
On Sunday, 4 February 2024 at 09:30:58 UTC, Jonathan M Davis wrote:
> The advantage of that is that the result is cleaner code (since __traits tends to be pretty ugly), but it also means more template instantiations. So, I expect that there is going to be some disagreement on what we should be doing there

Man this is such an easily-fixable problem. I wish Walter weren't so stubborn about having macros in D. All it would take is something like:

```
macro template isNumeric(T) {
    __traits(isNumeric, T)
}

enum isStringNumeric = isNumeric!String;
assert(!isStringNumeric);
```

No symbol template instantiation or symbol generation necessary. It's like mixin templates but in reverse. Constrain macro templates to only be allowed to expand to an expression or something. It completely fixes any issues with std.traits, and is probably useful in a lot more areas as well.

February 05

On Sunday, 4 February 2024 at 01:01:28 UTC, Adam Wilson wrote:

>

JMD and I were talking about sockets and I think he came up with an interesting approach to this problem. He built a low-level API that is either BetterC compliant or very close to it, and then built a higher level API on top of that. The low-level API is painful to use but it allowed him to build the high-level API he wanted in a way that made sense. You can use the low-level API if you absolutely need that level of performance/control/etc, but most projects will find the high-level API to be sufficient for their needs.

Perhaps this is a concept that we should investigate for other areas of Phobos?

Absolutely.

A lot of things in Phobos unnecessarily use classes. I feel very strongly that V3 should go in the direction of using structs where possible, non-copyable/scoped/unique ones if needed.

February 05
On Sunday, 4 February 2024 at 23:16:36 UTC, H. S. Teoh wrote:
> On Sun, Feb 04, 2024 at 09:10:20PM +0000, Sebastiaan Koppe via Digitalmars-d wrote: [...]
>> I personally find exceptions useful only when I want to jump many stack frames at once.
>
> But I thought exceptions were not intended to be used in that kind of way. They're more like an abort condition to short-circuit the normal flow of control. If a failure condition is an expected part of the program, throwing an exception is probably not the best way of implementing it.

Sorry, I didn't phrase it correctly. You are right, exceptions obviously aren't for control flow.

I meant that I find them very useful when I'm in the bowels of a library and need to bubble up an error to the original caller. With exceptions you can do this without involving the middle layer; just throw and let someone else care about it.

This is very convenient. However, that is about the only thing that they have going for me.

>> Overall I find them taxing because they add another possible 'return' value to a function. Sometimes its documented, but 9 out of 10 times you'll find out at runtime.
>
> But isn't it enough to just have a blanket catch block in the dispatch code to swallow any exceptions that downstream code might generate? You don't have to know exactly what it is, you just catch it, abort the offending task, and move on to the next one?

Sometimes that can work yes.

I find that when I'm writing server software however, I often want to handle a subset of the possible failures. That might involve a blanket retry, a retry with tweaked parameters, a possible fallback, etc.

Using algebraic datatypes has always been a lot more explicit for me.

>> Note I often write server programs. YMMV with e.g. batch programs.
>
> There are certainly valid considerations about the predictability of control flow. I.e., if you call a series of functions in a row and they are all nothrow, you can rest assured that the control flow will reach the end of the block, and not bail out on you in the middle.

Yes, exactly. I value that a lot.

> Still, when a fatal error happens deep inside some low-level code and there's nothing else you can do until you get back to the high-level business logic, exceptions are very useful. To manually bubble the error state all the way back up the call stack is just too onerous. It forces all intermediate levels of code to be dependent on the exact error codes / sumtypes so that it can be properly propagated up to the level where the code has enough context to react to it meaningfully. This increases coupling between orthogonal code. Using exceptions avoids this spurious coupling.

With exceptions there is coupling as well. In fact, I would argue the coupling is worse since its over a wider distance. Plus you have to 'discover' them over time.

It is true that it can sometimes be a bit onerous. In practice its only in a few rare cases, so I do it and move on. The possible set of failures is pretty static so its not like you have to extend the set all the time.

---

To conclude, I guess I consider exceptions to be very unstructured. Not only do they rarely contain the right information in order to decide the next step (unless you want to parse strings), because it is an open hierarchy, basically anything goes and you have no idea what gets thrown at you.

Not to mention that a refactor might change or add an exception, and there is no way you get informed about that during compilation.