against enforce

spir denis.spir at gmail.com
Fri Mar 25 13:23:08 PDT 2011


On 03/25/2011 08:21 PM, Jonathan M Davis wrote:
>>> So, there really is no good answer.
>>> - Jonathan M Davis
>>
>> So why do you need to differentiate between assert and enforce if you can't
>> choose, which of them should be used?
>>
>> We can't really turn off both of them, and if we really want performance
>> and no checks, we would want to turn off both of them, so they should work
>> in the same way, shouldn't they?
>
> assert and enforce serve _very_ different purposes. assert is for checking the
> logic of your code. It can go away, so you can't rely on it. It's simply for
> additional checks in your code to ensure that it's of high quality. You can't
> rely on it. It's also _not_ for error handling. When an assertion fails, there
> is a bug in your program.
>
> Exceptions - and therefore enforce - are for error handling. They are supposed
> to _always_ be there. Performance has nothing to do with them (other than the
> fact that they can obviously harm performance, which may cause you to refactor
> your code so that they're not necessary). Typically, exceptions - and
> therefore enforce - are used when validating input. That input is often
> completely dependent on the particular run of the program, and bad input isn't
> necessarily a bug in the program at all. When enforce fails, that does _not_
> necessarily indicate a bug in your program, and it should _not_ be used for
> finding bugs.
>
> Input from the user is obviously always input, and you're going to have to
> check that and throw an exception on failure rather than use assertions. Input
> to a function which is completely local to your code is not in any API
> anywhere and whose input is completely controlled by your code should use
> assertions. At that point, if the function gets bad input, it's a bug in your
> code. Also, out blocks, invariants, and checks in the middle of functions
> typically have _nothing_ to do with input and should be assertions. If they
> fail it's a logic bug.
>
> The problem is when a function could be both used internally and used on user
> input. For instance, iota is typically given hard-coded values - iota(5, 100,
> 2) - but you could pass it value which was given to main - iota(5, 100,
> to!int(args[1]). With hard-coded values, assert is the correct solution. But
> with user input, enforce would be. So, which do you do? assert or enforce?
>
> In the case of iota, since it is almost always used with hard-coded values and
> even when it isn't, it's likely used with computed values rather than user
> input, so if it's wrong, it's a bug in the code rather than bad user input.
> The application can check (with enforce or with an if and throwing an
> exception or whatever) that the input is good before passing it to iota if
> that's what it's doing.
>
> With other functions though, it's less clear. And with every such function, a
> choice must be made. Should it treat its input as user input or as values
> local to the program? If it's user input, then it needs to use exceptions. If
> it's local to the program (at which point a bad value would be a bug in the
> program), then assert should be used. And when you're dealing with a library,
> it's not as clear what the best solution should be in. The fact that
> assertions are almost certainly going to be compiled out might make it so that
> you want to treat input to the library's API as user input rather than local
> to the program when you would choose to have those same functions be treated
> as local to the program if they weren't in another library (though of course,
> there are plenty of cases where API functions should just plain be treating
> input as user input regardless).
>
> So, there is a clear and distinct difference between the intended uses of
> assert and exceptions (and therefore enforce). They have very different roles.
> The question then is not what their roles are but what you need a particular
> function to do - e.g. treat it's input as user input or treat it as local to
> the program (and therefore a bug if it's wrong).

This logic certainly looks sensible, but I cannot understand how it should work 
in practice. Say I'm implementing a little set of operations on decimals. Among 
those, some (division, square root...) will necessarily have to check their input.
According to the rationale you expose, I should use assertions, since operand 
will nearly never be (direct) user input, instead be products of the app's 
logic. Then, what happens when div gets a null denominator on a release build? 
In practice, the issue is not that serious since I will certainly delegate to a 
lower-level func which itself throws. But I could also (in theory) implement it 
in assembly or whatnot.
My point of view is if a func has preconditions on its args, then checkings 
simply cannot go away.

Such considerations lead me to wonder whether we should not instead use 
exceptions/enforce everywhere for actual func arg checking, and use asserts in 
unittests only. Or use them also for /temporary/ additional checkings during 
development (similar to unittests in fact).

A special case may be about checkings that control logical values or ranges 
which do not prevent the func to run. Say, a.length should logically be in 1..9 
-- but the func can run fine anyway.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d mailing list