Thread overview
assert with format considered harmful
Feb 26, 2017
Ali Çehreli
Feb 26, 2017
Seb
Feb 26, 2017
ketmar
Feb 26, 2017
Seb
Feb 26, 2017
Jonathan M Davis
Feb 27, 2017
Ali Çehreli
February 25, 2017
If the generation of the error message throws, then the program receives a FormatException, not the most import AssertError:

import std.string;

void foo(int i) {
    // In this case a %s is forgotten but it could be any other trivial error.
    assert(i == 42, format("Bad parameter:", i));
}

void main() {
    foo(43);
}

Opened:

  https://issues.dlang.org/show_bug.cgi?id=17226

So, obviously, assert message generation is not lazy. This is a WAT! for me but perhaps there is a good reason for it.

Google found a related article by Peter Alexander:

  http://poita.org/2012/09/02/a-better-assert-for-d.html

Ali
February 26, 2017
On Sunday, 26 February 2017 at 06:34:07 UTC, Ali Çehreli wrote:
> So, obviously, assert message generation is not lazy. This is a WAT! for me but perhaps there is a good reason for it.

FWIW imho we shouldn't need to write such messages at all.
It shouldn't be to difficult to lower `assert (a BINOP b)` into sth. like:

(auto ref a, auto ref b) {
    if (a BINOP b) return;
    onAssertFailed!"BINOP"(a, b, __FILE__, __LINE__, __FUNCTION__, __MODULE__);
} (e1, e2);

with onAssertFailed being a nice pretty-printer in the direction of:

assert([1,2,3] == [1,2,4]); // ERROR: ([1,2,3][2] is 3) != ([1,2,4][2] is 4)
struct A { int x, y; }
auto a = A(1,2);
auto b = A(1,3);
assert(a == b);  // ERROR: (a.y is 2) != (b.y is 3)

This idea is formally known as DIP83:

https://wiki.dlang.org/DIP83
February 26, 2017
Seb wrote:

> On Sunday, 26 February 2017 at 06:34:07 UTC, Ali Çehreli wrote:
>> So, obviously, assert message generation is not lazy. This is a WAT! for me but perhaps there is a good reason for it.
>
> FWIW imho we shouldn't need to write such messages at all.
> It shouldn't be to difficult to lower `assert (a BINOP b)` into sth. like:
>
> (auto ref a, auto ref b) {
>      if (a BINOP b) return;
>      onAssertFailed!"BINOP"(a, b, __FILE__, __LINE__, __FUNCTION__,      __MODULE__);
> } (e1, e2);
>
> with onAssertFailed being a nice pretty-printer in the direction of:
>
> assert([1,2,3] == [1,2,4]); // ERROR: ([1,2,3][2] is 3) != ([1,2,4][2] is 4)
> struct A { int x, y; }
> auto a = A(1,2);
> auto b = A(1,3);
> assert(a == b);  // ERROR: (a.y is 2) != (b.y is 3)
>
> This idea is formally known as DIP83:
>
> https://wiki.dlang.org/DIP83

or at least print the condition that failed, if there is no assert message. this patch alone saved me alot of brain cells. somehow it is way easier for me to look at stack trace and get "aha!" moment, than to look at the source line with assert. even if the info is exactly the same. ;-)

besides, assert with condition printed simply looks better.

note that your suggestion may require calling `toString()`, which may allocate, and it may be undesirable to allocate there (maybe programmer did't printed more detailed info exactly 'cause he wanted to avoid possible allocations?). and condition printing done by simply adding pretty-printed string in frontend.
February 26, 2017
On 02/26/2017 02:17 AM, Seb wrote:
> On Sunday, 26 February 2017 at 06:34:07 UTC, Ali Çehreli wrote:
>> So, obviously, assert message generation is not lazy. This is a WAT!
>> for me but perhaps there is a good reason for it.
>
> FWIW imho we shouldn't need to write such messages at all.
> It shouldn't be to difficult to lower `assert (a BINOP b)` into sth. like:
>
> (auto ref a, auto ref b) {
>      if (a BINOP b) return;
>      onAssertFailed!"BINOP"(a, b, __FILE__, __LINE__, __FUNCTION__,
> __MODULE__);
> } (e1, e2);
>
> with onAssertFailed being a nice pretty-printer in the direction of:
>
> assert([1,2,3] == [1,2,4]); // ERROR: ([1,2,3][2] is 3) != ([1,2,4][2]
> is 4)
> struct A { int x, y; }
> auto a = A(1,2);
> auto b = A(1,3);
> assert(a == b);  // ERROR: (a.y is 2) != (b.y is 3)
>
> This idea is formally known as DIP83:
>
> https://wiki.dlang.org/DIP83

Yea. Six years ago, assertPred was written for Phobos and rejected because it was decided it was better for assert to just gain that functionality built-in...which a full six years later, never happened. Letting perfect be the enemy of the good, at its golden finest.

February 26, 2017
On Sunday, 26 February 2017 at 16:13:47 UTC, Nick Sabalausky (Abscissa) wrote:
> On 02/26/2017 02:17 AM, Seb wrote:
>> [...]
>
> Yea. Six years ago, assertPred was written for Phobos and rejected because it was decided it was better for assert to just gain that functionality built-in...which a full six years later, never happened. Letting perfect be the enemy of the good, at its golden finest.

Well if it makes you feel any better, I had a similar experience last summer:

https://github.com/dlang/phobos/pull/4323

(the same opinion prevailed)
February 26, 2017
On Sunday, February 26, 2017 11:13:47 Nick Sabalausky  via Digitalmars-d wrote:
> Yea. Six years ago, assertPred was written for Phobos and rejected because it was decided it was better for assert to just gain that functionality built-in...which a full six years later, never happened. Letting perfect be the enemy of the good, at its golden finest.

Well, to make matters more entertaining, it was later decided that it was unreasonable to alter assert to print the extra information that assertPred had. So, it's not even a case of no one doing it. It's a case of it being rejected, leaving us with neither solution.

- Jonathan M Davis

February 27, 2017
Let me point out the elephant in the room as well.

On 02/25/2017 10:34 PM, Ali Çehreli wrote:

>     // In this case a %s is forgotten but it could be any other trivial
> error.
>     assert(i == 42, format("Bad parameter:", i));

In addition to the current dangerous behavior of assert, there is the philosophical argument whether the program can do anything at all after an assert. Like many others, our friend Chris Wright thinks so:

  http://forum.dlang.org/post/o6u5j5$1gnf$1@digitalmars.com

On the other hand, I, like many others, have apparently been hypocritical every time I attempted to generate a message for assert. That's wishful coding indeed. :)

How about this rule: The message generation code must be

- be nothrow

- have no side-effects, which means the message that it generates must be written into a pre-allocated space, without going out of bounds (how to control this?)

- nogc (I think this is implied from the previous two)

The more I learn and understand about computing the more it becomes crazy. :)

Ali