REBOL 3.0

Comments on: ASSERT function

Carl Sassenrath, CTO
REBOL Technologies
22-Mar-2009 0:15 GMT

Article #0178
Main page || Index || Prior Article [0177] || Next Article [0179] || 11 Comments || Send feedback

Do you find you often need to check for valid variable values? Me too.

For example, here is part of the make-module intrinsic that checks important fields of a module's specification:

; Validate the important fields:
unless all [
    object? obj: :spec
    block?  obj: :body
    any [
        none? obj: spec/name
        word? spec/name
    ]
    any [
        none? obj: spec/version
        tuple? spec/version
    ]
][
    print ["error in module at:" obj] ; temp
    cause-error 'script 'invalid-spec :obj ; something is wrong here!
]

It's just a bit wordy, isn't it?

That code has just been replaced with the assert native and now looks like this:

assert/type [
    spec object!
    body block!
    spec/name [word! none!]
    spec/version [tuple! none!]
]

It's a bit like a function spec, isn't it? Of course, it also provides the simplified datatype form, as seen in the first two words.

Here's more information about the assert function, which will be released in R3 A38.

Note that assert is very efficient because it does not need to reduce. In addition, it does a simple get on variables; therefore, avoiding problems with possible function evaluation (i.e. it is secure). That means you can use it at the entry point to any section of code that might go wrong if functions are passed.

This is another one of those I've been putting it off for years, but it's used often and worth adding to the language.

11 Comments

Comments:

Sunanda
23-Mar-2009 11:44:31
Looks nice.

One extra refinement, please -- a user specifiable identifier. Then, when the assert failure is reported, we have some idea where the bad code is:

    assert/id [num > 20] "module: a123 function: update-db"

** Script error: assertion failed for: [num > 20] ** Where: assert id: "module: a123 function: update-db" ** Near: assert [num > 20]

Robert
23-Mar-2009 15:19:39
And please output which assert check failed (number of check in order).

And yes, this is a very good thing to add!

Book Siberia
23-Mar-2009 16:24:19
Starting from

age: "37"
name: "Bob"
assert/type [age integer! name string!]
** Script error: datatype assertion failed for: age
** Where: assert
** Near: assert/type [age integer! name string!]

Why not add the following very useful refinement?

example:1) assert/type/enumerate-all
age: "37"
name: "Bob"
assert/type/enumerate-all [age integer! name string!]
** Script error: datatype assertion failed for: age
** Where: assert
** Near: assert/type [age integer! name string!]
** ---------------------------------------------------
** age string! type-error
** name string! valid-type
** ---------------------------------------------------

could this be done?

Carl Sassenrath
23-Mar-2009 17:19:54
Replies to above:

1. In R3, the stack backtrace is a standard part of errors, and it is easy to identify where errors happen (compared to R2). An id is possible, but is it really necessary?

Example:

 f1: func [t] [assert [t > 20]]
f2: func [s] [f1 s]

f2 1 ** Script error: assertion failed for: [t > 20] ** Where: assert f1 f2 ** Near: assert [t > 20]

2. Yes, the ASSERT error shows you the failing assertion:

a: 1
b: 2
assert [a > 0 b > 10]
** Script error: assertion failed for: [b > 10]
** Where: assert
** Near: assert [a > 0 b > 10]

3. It is possible, but it is easy enough (given what I've noted above) to see that information, and we want to keep errors as simple in format as possible.

Brian Hawley
23-Mar-2009 18:12:49
I look forward to cleaning up the mezzanines - there is a lot of screening code that could be replaced with ASSERT calls.
Gregg Irwin
23-Mar-2009 19:58:43
Yay for more support tools in REBOL!

It looks like follows the strict Eiffel-inspired model of "assertions are not input checking mechanisms", but is it the right thing for REBOL?

* What happens in the field, in an encapped app, when an assertion fails? i.e., is there any way to turn them off or log the results?

* How would I use them to build a higher level system? e.g. pre and post conditions for functions, that I can access via reflection.

* Can I leverage it, catching the error and parsing the error message, or via a redirection mechanism¹, to press it into service for input checking or a test engine? Maybe a dialect over it that associates a message to display for each condition.

¹ I've hacked HELP and SOURCE so I can use the results programmatically. I'd guess others have as well.

Peter Wood
23-Mar-2009 20:18:43
Automatically throwing an error when an assertion fails does suggests that the new Assert function should not be used for data validation but only for non-recoverable errors.

If your intention is to provide a shorthand for data validation then perhaps the standard behaviour of Assert should be to return true or false. An error refinement could be provided for checking for non-recoverable errors.

Brian Hawley
24-Mar-2009 17:56:13
Gregg, here are some proposed answers to your questions (as if you numbered them):

A1: Turning them off: assert: :comment

A1: Logging the results: Same as any other error.

A2: Using ASSERT to build pre/post conditions: Don't. Use function code block generators or function generators instead. ASSERT is lower-level than a full design-by-contract system. The input dialect of ASSERT could be reused for a DbC system though, and maybe the function could be used in the code generated by the DbC function generator.

A3: Same as any other error. ASSERT is for generating nonrecoverable errors, and a test framework that logs errors should be able to log ASSERT errors just like any other error.

Brian Hawley
24-Mar-2009 17:59:12
Peter, the error generation behavior of ASSERT is more useful. We already have your proposed TRUE/FALSE generating ability covered by other functions.
Gregg Irwin
25-Mar-2009 1:50:08
Brian, thanks for your thoughts. I'm really looking for, or provoking, Carl's thoughts. If you're stating what he has said elsewhere, cool, I have my answer.

I'm not sold on it as proposed though.

Carl Sassenrath
26-Mar-2009 16:14:51
I like simple functions to be simple. Although that's not always possible (e.g. LOAD), it is the goal. An old Unix principle.

ASSERT is a simple, lower-level "mask filter" that throws errors.

Don't read anything into ASSERT other than the simplistic check it performs. As I wrote above, it's purpose is to check assertions that should always be TRUE before proceeding into code that assumes such conditions. (Thus removing extra checks that would pollute the readability of that code.)

Do not use it for higher level functions! It is not for parsing or validation of arbitrary arguments or dialects. For that, we need something quite different, and we don't want to merge those features into the same function.

Post a Comment:

You can post a comment here. Keep it on-topic.

Name:

Blog id:

R3-0178


Comment:


 Note: HTML tags allowed for: b i u li ol ul font span div a p br pre tt blockquote
 
 

This is a technical blog related to the above topic. We reserve the right to remove comments that are off-topic, irrelevant links, advertisements, spams, personal attacks, politics, religion, etc.

REBOL 3.0
Updated 28-Mar-2024 - Edit - Copyright REBOL Technologies - REBOL.net