On the subject of error messages

Stanislav Blinov via Digitalmars-d digitalmars-d at puremagic.com
Tue May 16 11:29:36 PDT 2017


On Tuesday, 16 May 2017 at 15:47:37 UTC, Steven Schveighoffer 
wrote:
> On 5/16/17 9:54 AM, Stanislav Blinov wrote:
>> On Tuesday, 16 May 2017 at 12:27:30 UTC, Steven Schveighoffer 
>> wrote:
>>
>>>> When we have tests using dummy lambdas, are we to expect 
>>>> users to
>>>> immediately extract the lambda body, parse it, and figure 
>>>> out what's
>>>> wrong?
>>>
>>> This is what you have to do today. The task has already been 
>>> tried by
>>> the compiler, and the result is known by the compiler. Just 
>>> have the
>>> compiler tell you.
>>
>> :) The compiler does not know what I'm checking for with that 
>> lambda. As
>> far as the compiler is concerned, I'm interested in whether it 
>> compiles
>> or not. It doesn't care what that means in the context of my 
>> constraint.
>> Neither should the user.
>
> You seem to be not understanding that a hand-written message of 
> "needs to have member x" or something conveys the same 
> information as the compiler saying "Error in this line: auto 
> test = T.x"

You are nitpicking again, or, at least, oversimplifying. I am 
understanding that perfectly. But we've both shown examples of 
constraints that are much more complex than just checking for one 
member. As in, the type needs to have these members and/or these 
properties. All of them. Or some of them. Or a set of 
combinations of them. With those specific traits, etc. It's not 
*just* "error in this line". It's "error in this line on this 
execution path with these conditions". And nowhere in sight is 
*what* all those checks are doing as a unit. Not individually, 
but in complex, all of them, in concert.
A fresh user of your code cannot know at a glance what all of 
them are doing, nor should they.

> Sure, it's not proper English. It gets the job done, and I 
> don't have to instrument all my functions and template 
> constraint helpers. I don't need to explode compile times, or 
> debug why my messages don't match what actually happens.

If your messages don't match what actually happens, there's a bug 
in your constraint, simple as that. Which you should catch with 
tests.

>>> The compiler, and by extension your hand-written error 
>>> checking,
>>> cannot know the true intention of the user.
>>

> hand-written == hand-compiled. That is, I translated what I 
> think this boolean condition means into a human-readable format 
> by hand. I could have got it wrong. Then a nice message is 
> useless.

Eh? You *wrote* the constraint. You *know* what all of those 
tests mean. You didn't translate what you *think* the condition 
means. You wrote the condition, you *know* what it means. If you 
don't, you shouldn't be writing this constraint. The only way for 
you to "get it wrong" is to write a bug in the constraint. Or in 
the compiler.
You know that if you've just did is(typeof((T t) @safe {})) and 
it returned false, T's destructor cannot be called in @safe code. 
It's obvious, isn't it? Every D user knows that for sure.
There are obscure checks, that's the way the language works. 
There are cases when those obscure checks need to be cleanly 
explained. That is *all* that I'm proposing.

>> When the
>> user violates the contract, I must inform them they did so, by 
>> reporting
>> *why* their argument does not satisfy me. Not by telling *how* 
>> I figured
>> that out (compiler output), but by telling *what* is wrong 
>> with the
>> argument (human-readable error message).
>
> The constraint tells the user why it doesn't work. There is no 
> extra effort required. It's human readable (I know what 
> isInputRange!R means). I don't need a specialized full-sentence 
> message to understand that.

*You* know what isInputRange means. This doesn't mean that all 
users do.
And do you know, beforehand, what all possible constraints in all 
possible libraries that you may use in the future mean, without 
looking at their code? No. No one does.

>> If the user does want to know how the constraint caught their 
>> mistake,
>> they're free to inspect what the compiler outputs.

> And here is the problem. Your solution doesn't get us any 
> closer to that. It's actually quite painful to do this today.

Of course it is painful, that's the whole point of my proposal.

> When I have a type like this:
>
> struct S
> {
>    int foo;
> }
>
> and the hand-written error message says: "Your type must be a 
> struct that contains an integer property named 'foo'". How does 
> that help me figure out what I did wrong? We can spend all day 
> arguing over how nice this message is, but the truth is, what 
> the constraint writer put in the constraints, and how the 
> compiler is interpreting it, may be 2 different things. This 
> helps nobody.

It cannot be that way. Whoever wrote the constraint possesses 
knowledge about it's semantics. If they put wrong message there, 
then that's a bug, or they're a... well, not a very good person.

>> That is why (static) asserts have an optional string argument: 
>> to
>> display a clean an readable error message, instead of just 
>> dumping some
>> code on the user. The same thing, in my opinion, is needed for 
>> constraints.
>
> If assert(x == 5) would just print "Error: x == 4" 
> automatically, we could eliminate most needs for a message.

You're taking overly simplified pieces of code and base your 
reasoning on them, while you know full well it's almost never 
this simple.

assert(x.veryBadlyNamedFunction == 
obscureStateObtainedFromElsewhere);

"Error: x.veryBadlyNamedFunction == 42"

Very helpful message.

> You could solve this with a message, but again, this is a huge 
> task to undertake on ALL code in existence, rather than fixing

I seem to be expressing myself poorly, as I find I must repeat it 
the second time already: I never suggested to change any existing 
code. I suggested an *optional* mechanism of reporting errors.

> it all to a "good enough" degree that we don't need the 
> messages. And it avoids the "human compiler" that is prone to 
> error.

This again. No it does not. If there's a bug in the constraint, 
there is a bug in the constraint. Messages or no, it's all the 
same. Either it does what it's supposed to do, or it doesn't. 
Reporting the wrong message is a bug, as is returning false for 
compliant type, or true for malicious one.

>> stack traces. User is informed their type is wrong and *why* 
>> it is
>> wrong. If they disagree, if they think there's a bug in the 
>> constraint,
>> or if they're interested in how the constraint works, they're 
>> free to go
>> through all the hoops you describe.
>
> That doesn't help me if the constraint author didn't put that 
> message, or the constraint author put the *wrong* message, or 
> wrote the constraint incorrectly.

And again. If they wrote the constraint incorrectly, it's their 
bug. If they didn't put any message, compiler still should 
provide it's own diagnostics (better than today ones for sure).



More information about the Digitalmars-d mailing list