Proposal: Why not add assertion/2?

Currently we only have assertion/1 from library(debug) described as

assertion(:Goal)

which throws an exception with a (non-ISO standard) formal term assertion_error/2 if the Goal fails.

In fact, the exception thrown in case of Goal equal to false is

error(assertion_error(fail,user:false),_4750)

This is similar to Java’s assert(expr) (first added in Java 1.4 in 2002 only btw.)

I suppose assertion/1 can be compiled-out using expand_goal/2, if the Goal is too expensive at runtime; I haven’t tried that yet.


It would be useful to have an assertion/2 described as:

assertion(:Goal,:Closure)

where Closure is executed when Goal fails with one additional uninstantiated argument, which shall be instantiated by the Closure, and which inserted into the second argument (the context term) of the error/2 term:

error(assertion_error(fail,user:false),T)

Then one could generate good error messages, similar to Java’s assert(expr,msg):

Example:

Call point:

assertion(X<Y,assertion_handler(X,Y)).

And handler:

assertion_handler(X,Y,T) :- 
   with_output_to(string(T),format("This is bad. ~q < ~q; this rocket's gonna crash!!",[X,Y])).

Why not use a predicate with two clauses?

See recent post: How to identify if a predicate is a DCG?

build_export(M,Exports) :-
    current_module(M), !,
    setof(Predicate_indicator,local_predicate_generator(M:Predicate_indicator),Exports).
build_export(M,_) :-
    format(string(Lines),'Module `~w'' not loaded.~nPlease use consult/1 or use_module/1 to load the module.~n',[M]),
    print_message_lines(user_error,kind(error),[Lines]).

Why not use a predicate with two clauses?

Ah, but this wouldn’t help me in getting the informative term I’m generating in the Closure into the exception term, which is thrown by assertion/1. The idea is to have the information in the exception, not on STDERR. Besides, the assertion/1 call can appear anywhere inside a clause, maybe multiple times.

Then why not pass an accumulator around to hold the state for the error messages? You could then use DCGs? No?

Not an accumulator. Just terms that are of interest to the error-text generating closure. There may be no arguments at all:

assertion(X<Y,=("The usual Sunday evening exception")).

Ok, I don’t know whether that would work and it’s also somewhat ugly, but the idea is to unify the term T that will be transported by the exception term with a constant message, so the following is thrown:

error(assertion_error(fail,user:(2<1),"The usual Sunday evening exception")

With assertion/1 one just gets

error(assertion_error(fail,user:(2<1)),_)

My understanding of assertions, regardless of programming language, is that during development they are there to check that you are meeting certain requirements, typically on entry and exit of a procedure. They are not there to do the heavy lifting and are typically a predicate, regardless of programming language, to signal the condition is valid.

When the code reaches a production level they are typically removed; the condition for being considered production code is the optimize option. SWI-Prolog does the same with debug/3.

If you want detailed error messages then I would suggest you look for other means. :grinning:

Well, you would really use them to check internal invariants too and I always recommend to leave assertions on unless they are costly, but yeah.

Take a look at the Power Assertions in Groovy:

http://docs.groovy-lang.org/next/html/documentation/core-testing-guide.html

def x = [1,2,3,4,5]
assert (x << 6) == [6,7,8,9,10]

// Output:
//
// Assertion failed:
// assert (x << 6) == [6,7,8,9,10]
//         | |     |
//         | |     false
//         | [1, 2, 3, 4, 5, 6]
//         [1, 2, 3, 4, 5, 6]

Probably going too far.

Still, adding a generated message if there is failure of the assertion predicate is not “heavy lifting”. It’s just adding a message? Let’s see if I can code that.

Here’s how Ciao Prolog does it:

https://ciao-lang.org/ciao/build/doc/ciao.html/assertions_doc.html

Have you looked for or seen:
Package assertions - Ciao Assertions Reader for SWI-Prolog
Package ciao - Ciao Prolog compatibility library
Package dialect-ciao - Ciao Prolog compatibility library

I have not done anything more with them than read the description and note it here.

Some of the authors are
@edison
@jromme

Two remarks. Ciao assertions are very different from SWI-Prolog assertions. They merely share the name. SWI-Prolog assertion/1 is modeled after C assert/1, while Ciao are declarations about the behavior of a predicate (types, pre- and post conditions and some more). Ciao conditions were originally mostly intended for static analysis. @edison started? using them to insert runtime checks. I may be a little off, so please correct where I’m wrong :slight_smile:

As for SWI-Prolog’s assertion/1, its behavior can be changed at various levels: the hook prolog:assertion_failed/2 can be used to define what happens. The default is an exception which you catch or for which you can change the way it is printed. I think that is enough.

1 Like