June 26, 2017
On Monday, 26 June 2017 at 15:40:19 UTC, mckoder wrote:
> On Monday, 26 June 2017 at 15:15:54 UTC, Steven Schveighoffer wrote:
>>
>> No, checked exceptions leads to this (maybe not for you, but for 90% of developers out there):
>>
>> void foo()
>> {
>>    functionWithException();
>> }
>>
>> compiler: foo throws, and you need to handle or declare the exceptions it throws
>>
>> void foo()
>> {
>>    try {
>>      functionWithException();
>>    } catch(Exception e) {} // shut up compiler
>> }
>>
>
> Why wouldn't you instead write:
>
> void foo() throws Exception // shut up compiler
> {
>    functionWithException();
> }
>
> That's easier, and no worse than C# even though you have defeated checked exceptions.

Because that's even worse.
The `try catch` as least takes responsibility for handling the error - even if it is just intentionally ignoring it.
Your version delegates the responsibility up the call chain (which is in and of itself fine), but it - as you even stated - prevents the caller from using checked exceptions, i.e. you're cheating the system.

> Here's the point: with checked exceptions good programmers can write good code.

With checked exceptions any programmer is forced to
a) annotate every single function with the complete aggregate of the exceptions that may be thrown by itself or the functions it calls
b) violate checked exceptions and limit its callers by marking itself as throwing a parent exception

Or, more succinct: You must either manually write things down the compiler could find out in a fraction of the time via static analysis, or cheat the system; both cases are bad code.
June 26, 2017
On Monday, 26 June 2017 at 16:52:22 UTC, Sebastien Alaiwan wrote:
> Checked exceptions allow a lot more precision about what types of exceptions a function can throw.

I totally agree that this is a problem with D right now.  If you want to catch all errors, how are you supposed to remember what std.file.readText throws?  Or std.file.mkdir?

There are two solutions to this problem that I know of:

A) Checked exceptions
B) Error codes that can't be implicitly ignored

Java uses A, Rust/Go use B.  C++ uses B to some extend (e.g. in std::experimental::filesystem).

In my opinion, option B better than A because checked exceptions are incredibly verbose.  However, both are better than nothing (which is the current state of D right now).

It is very well possible to use option B in D.  The most convenient one is making functions nothrow and use Algebraic!(T, ErrorCode), or, for void functions, have a parameter "ref ErrorCode".
If all functions in Phobos would either follow that pattern or provide an alternative nothrow overload, I would consider that problem solved.
June 26, 2017
On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher wrote:
> It is very well possible to use option B in D.  The most convenient one is making functions nothrow and use Algebraic!(T, ErrorCode), or, for void functions, have a parameter "ref ErrorCode".
> If all functions in Phobos would either follow that pattern or provide an alternative nothrow overload, I would consider that problem solved.

I have tried using such Monads in D, but in the end it always ended up being too verbose or too hard to read compared to using exceptions or even simple error codes (with 0 == no error).
June 26, 2017
On Monday, 26 June 2017 at 17:43:08 UTC, Moritz Maxeiner wrote:
>> Here's the point: with checked exceptions good programmers can write good code.
>
> With checked exceptions any programmer is forced to
> a) annotate every single function with the complete aggregate of the exceptions that may be thrown by itself or the functions it calls
> b) violate checked exceptions and limit its callers by marking itself as throwing a parent exception
>

Here's an example C# pseudocode to illustrate the problem:

Programmer A writes this C# code:

class A {
   public static void startFoo() {
      if (/* Foo is not installed */)
          throw new FooNotInstalled();
      // ...
   }
}

Programmer B calls the above code like this:

class B {
   try {
      A.startFoo();
   }
   catch (FooNotInstalled) {
      // Tell user to purchase Foo
   }
}

Later programmer A updates his code because there are newer versions of Foo and he needs the newest version:

class A {
   public static void startFoo() {
      if (/* Foo is not installed */)
          throw new FooNotInstalled();
      if (/* Foo version is too old */)
          throw new FooVersionTooOld();
      // ...
   }
}

Now the code written by Programmer B crashes even though it compiles file. That's bad.

Had this been Java, programmer would be would be alerted to the fact that he needs to decide what do do if the version of Foo is too old. This is good.

So listing exceptions that can be thrown is a good thing because it helps you write more reliable code. If you are lazy you can always defeat the system by declaring your method as throwing a parent (or the root) exception class, in which case it is no worse than C#.

June 26, 2017
On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher wrote:
> Java uses A, Rust/Go use B.  C++ uses B to some extend (e.g. in std::experimental::filesystem).

The C++17 filesystem api provides two alternatives, the standard filesystem_error exception and an output-paramater for capturing os-specific error codes. I'm not quite sure why they provide both, but I guess performance and the ability to compile for  runtimes with exceptions turned off could explain it.

It is rather clear though that C++ std lib relies heavily on exceptions.

June 26, 2017
On Monday, 26 June 2017 at 18:31:50 UTC, jag wrote:
> On Monday, 26 June 2017 at 17:43:08 UTC, Moritz Maxeiner wrote:
>>> Here's the point: with checked exceptions good programmers can write good code.
>>
>> With checked exceptions any programmer is forced to
>> a) annotate every single function with the complete aggregate of the exceptions that may be thrown by itself or the functions it calls
>> b) violate checked exceptions and limit its callers by marking itself as throwing a parent exception
>>
>
> Here's an example C# pseudocode to illustrate the problem:

No need, you can assume the people discussing this are familiar with the issue, it's not new.

>
> class A {
>    public static void startFoo() {
>       if (/* Foo is not installed */)
>           throw new FooNotInstalled();
>       // ...
>    }
> }
>
> Programmer B calls the above code like this:
>
> class B {
>    try {
>       A.startFoo();
>    }
>    catch (FooNotInstalled) {
>       // Tell user to purchase Foo
>    }
> }
>
> Later programmer A updates his code because there are newer versions of Foo and he needs the newest version:
>
> class A {
>    public static void startFoo() {
>       if (/* Foo is not installed */)
>           throw new FooNotInstalled();
>       if (/* Foo version is too old */)
>           throw new FooVersionTooOld();
>       // ...
>    }
> }
>
> Now the code written by Programmer B crashes even though it compiles file. That's bad.
>
> Had this been Java, programmer would be would be alerted to the fact that he needs to decide what do do if the version of Foo is too old. This is good.

And the good *way* to achieve this result would be the following:
- When visiting `startFoo`, the compiler automatically aggregates all different exceptions it may throw and stores the resulting set
- If `startFoo` is going to be part of a (binary) library and its symbol is exported, also export its exception set
- Improve the compiler's nothrow analysis such that if startFoo is called in scope S, but all of the exceptions in its exception set are caught (i.e. can't break out of scope S), it is treated as nothrow in S.
- Enclose the call to `startFoo` in B in a nothrow scope.

>
> So listing exceptions that can be thrown is a good thing because it helps you write more reliable code.

It is a bad thing because you force a human to do a machine's job.

June 26, 2017
On Monday, 26 June 2017 at 17:50:47 UTC, Moritz Maxeiner wrote:
> I have tried using such Monads in D, but in the end it always ended up being too verbose or too hard to read compared to using exceptions or even simple error codes (with 0 == no error).

I haven't tried that yet, tbh.  visit is nice, but can't always be used.  So I guess unless D introduces syntax for pattern matching, it will always be verbose.

In that case a reference to an error code would be the most viable design in D.

June 26, 2017
On Monday, 26 June 2017 at 18:42:24 UTC, Ola Fosheim Grøstad wrote:
> On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher wrote:
>> Java uses A, Rust/Go use B.  C++ uses B to some extend (e.g. in std::experimental::filesystem).
>
> The C++17 filesystem api provides two alternatives, the standard filesystem_error exception and an output-paramater for capturing os-specific error codes. I'm not quite sure why they provide both, but I guess performance and the ability to compile for  runtimes with exceptions turned off could explain it.

Quoting the C++ standard:

> Filesystem library functions often provide two overloads, one that
> throws an exception to report file system errors, and another that sets an error_code.
> 
> [Note: This supports two common use cases:
>
>    - Uses where file system errors are truly exceptional
>      and indicate a serious failure.
>      Throwing an exception is an appropriate response.
>    - Uses where file system errors are routine
>      and do not necessarily represent failure.
>      Returning an error code is the most appropriate response.
>      This allows application specific error handling, including simply ignoring the error.
>
>  -- end note]

I would say that the overload without exceptions is the "standard" one.

> It is rather clear though that C++ std lib relies heavily on exceptions.

[Citation needed]

June 26, 2017
On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote:
> I am disappointed that D doesn't have checked exceptions.

I wonder what could be done with something like this:

void foo(int a)
{
    if (a > 0)
        throw new BlahException("blah");
    throw new BloopException("bloop");
}

unittest
{
    // NEW FEATURE HERE
    alias Exceptions = __traits(thrownTypes, foo);
    static assert (staticIndexOf!(BlahException, Exceptions) >= 0);
    static assert (staticIndexOf!(BloopException, Exceptions) >= 0);
}

I'm imagining one could use that to do quite a lot of what checked exceptions provide.
June 26, 2017
On Monday, 26 June 2017 at 21:53:57 UTC, John Colvin wrote:
> On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote:
>> I am disappointed that D doesn't have checked exceptions.
>
> I wonder what could be done with something like this:
>
> void foo(int a)
> {
>     if (a > 0)
>         throw new BlahException("blah");
>     throw new BloopException("bloop");
> }
>
> unittest
> {
>     // NEW FEATURE HERE
>     alias Exceptions = __traits(thrownTypes, foo);
> }
>
> I'm imagining one could use that to do quite a lot of what checked exceptions provide.

Yes, that would be a D idiomatic implementation of the function exception set I mentioned [1].

[1] http://forum.dlang.org/post/uovtkvpdagzagzhyacbp@forum.dlang.org