Speeding up DCD in big projects

Jacob Carlborg doob at me.com
Wed Jul 22 11:28:56 UTC 2020


On 2020-07-21 19:32, H. S. Teoh wrote:

> Ahh I see.  That makes sense then.
> 
> Which also begs the question, *why* does it even throw in the first
> place.  The non-existence of a file is a normally-expected outcome of
> isFile/isDir, throwing in that case seems excessively heavy-handed.

If it shouldn't throw, how should it deal with other IO errors than 
non-existing file/dir? What if it exists but you don't have permission? 
How do you tell the difference between the following conditions when it 
only returns a bool and doesn't throw:

* Exists and is a file // success
* Exists and is a file but you don't have permission // error
* Does not exist // error

There are probably other error conditions as well.

> It's probably a case of bad API design.

No, exceptions is the main error reporting system in D. Using any other 
system should have really good reasons to not use exceptions.

In my opinion, any other system (that I can come up with) results in bad 
and cumbersome APIs.

Returning error codes:
   Cons:
     * Steals the return channel
     * Forces to use out parameters
     * By default errors are not checked (the compiler won't force you 
do look at the return value)
     * Cumbersome to check errors
     * Cumbersome to propagate errors
     * Error handling code and regular code uses the same syntax
   Pros:
     * Better performance (some argue this is not true)
     * Does not require any runtime support

Returning an optional:
   Cons:
     * If an error occurred you only know that, not what error occurred
     * Cumbersome to check errors without language support
     * Cumbersome to propagate errors without language support
   Pros:
     * Error checking is better then error codes because you will be 
forced to check the error to get to the returned value
     * The return channel can still be used
     * No need to use out parameters

Returning a result object:
     * More or less the same as "optional" but with the advantage of the 
error that occurred is available
     * Another advantage over error codes is that it's usually possible 
to use an object for the error and not just an int

Cocoa error reporting:
     This model returns a bool to indicate error or not. It "returns" an 
error object through an out parameter. Similar to returning a result 
object, but since the out value and the error are separate the is no 
enforcement to check the error before accessing the out value

D Exceptions:
   Pros:
     * Leaves the return channel open for what it is intended for, 
returning values
     * Not necessary to use out parameters
     * Errors are handled one way or another. Either by the developer or 
by the runtime
     * Easy to propagate errors
     * Does not clutter regular code with error handling code
     * Standardized error system
   Cons:
     * Slow
     * Requires runtime support
     * Any function not marked with @nothrow may throw

In my opinion the best of solution would be to use the "result object" 
style of error reporting with syntax sugar supported in the language. 
This is what's proposed for C++ [1]. The advantage is that we can use 
the existing syntax which are used for exceptions. In D, it could look 
like this:

enum CopyError
{
     permissionDenied
}

void copy(string src, string dest) throw(CopyError)
{
     throw CopyError.permissionDenied;
}

void main()
{
     try
         copy("foo", "bar");
     catch (CopyError e)
         writeln(e);
}

Which would be lowered to something like the equivalent of the following 
code:

struct Result(Value, Error)
{
     bool isValue;
     union
     {
         Value value;
         Error error;
     }

     this(Value value)
     {
         this.value = value;
         isValue = true;
     }

     this(Error errro)
     {
         this.error = error;
         isValue = true;
     }
}

Result!(void, CopyError) copy(string src, string dest)
{
     return Result!(void, CopyError)(CopyError.permissionDenied);
}

void main()
{
     auto result = copy("foo", "bar");

     if (!result.isValue)
         goto L1;
     L1:
         writeln(result.error);
}


[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf

-- 
/Jacob Carlborg


More information about the Digitalmars-d mailing list