enforce()?

Michel Fortin michel.fortin at michelf.com
Wed Jun 16 09:06:16 PDT 2010


On 2010-06-16 10:53:12 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail at erdani.org> said:

> Leandro Lucarella wrote:
>> Steven Schveighoffer, el 16 de junio a las 06:55 me escribiste:
>>> On Wed, 16 Jun 2010 05:28:46 -0400, Ary Borenszweig
>>> <ary at esperanto.org.ar> wrote:
>>> 
>>>> On 06/16/2010 04:15 PM, Walter Bright wrote:
>>>>> Ali Çehreli wrote:
>>>>>> bearophile wrote:
>>>>>>> I have counted about 200 usages of std.contracts.enforce() inside
>>>>>>> Phobos. Can you tell me what's the purpose of enforce() in a language
>>>>>>> that has built-in Contract Programming?
>>>>>> I can see two benefits:
>>>>> The difference is not based on those 3 points, but on what Andrei wrote
>>>>> here. Contracts and error checking are completely distinct activities
>>>>> and should not be conflated.
>>>> Could you please explain them? There are many people here that
>>>> don't understand the difference between these two concepts
>>>> (including me). So maybe we are too dumb, maybe those concepts are
>>>> not generally known or maybe the explanation is not very well
>>>> clear in the documentation.
>>> I think of enforce as a convenient way translating an error in an
>>> expectation to an exception in a single expression.
>>> 
>>> For example, take some system call that returns -1 on error, you
>>> could do this:
>>> 
>>> if(result < 0)
>>>    throw new Exception("oops!");
>>> 
>>> or you could do this:
>>> 
>>> enforce(result >= 0, "oops!");
>>> 
>>> Think of enforce as "throw if"
>> 
>> So maybe throw_if() would be a better name =)
>> 
>> Anyway, I think enforce() is poisson,
> 
> Indeed it is a bit fishy :o).
> 
>> because it make the programmer to
>> not think about errors at all, just add and enforce() and there you go.
>> But when you need to be fault tolerant, is very important to know what's
>> the nature of the error, but thanks to enforce(), almost every error is
>> a plain Exception, no hierarchy, no extra info, all you can do to get
>> a little more info about what happened is to parse the exception string,
>> and that's not really an option.
> 
> I think there is no real need for exception hierarchies. I occasionally 
> dream of eliminating all of the useless exceptions defined left and 
> right in Phobos.

The need is not really for a hierarchy. The hierarchy serves the need, 
which is to:

1. Be able to programatically check the kind of the error and so your 
program can act appropriately.
2. Propagate additional information related to the error and the 
context it occured.

Displaying a proper error message to a user and offering relevant 
recovery choices often need both. Sometime, a program won't ask the 
user and attempt something by itself as a recovery. In both cases, you 
need to know the kind of error, and may need context information.


>>> And in fact, I think there's an errnoEnforce which throws a standard
>>> exception with the string error from the system.
>> 
>> That's the only useful case of enforce, because it includes the
>> *important* information (the actual errno).
>> 
>> There is also enforceEx!(), to use a custom exception, which practically
>> nobody uses (I counted only 4 uses in phobos).
> 
> I'd be hard pressed to find good examples of exception hierarchy use. 
> Everybody talks about them but I've seen none.

The need is not really for a hierarchy. The hierarchy serves the need, 
which is to:

1. Programatically check the kind of the error and so your program can 
act appropriately.
2. Propagate additional information related to the error and the 
context in which it occurred.

Displaying a proper error message to a user and offering relevant 
recovery choices often need both. Sometime, a program won't ask the 
user and attempt something by itself as a recovery. In both cases, you 
need to know the kind of error, and may need context information.

That said, hierarchies are often abused, and aren't universally useful. 
But exceptions should provide the above information in a way or another 
when useful.

Think about a GUI program, if an exception is thrown somewhere during a 
complex operation (say, reading a lot of files), I could catch it as 
some level, create a wrapper exception with the context (file=hello.d 
error=access denied) and rethrow it to unwind until reatching the GUI 
error handler. Or the file function could throw a useful exception from 
the start. In either cases, the code in charge of that operation can 
display a message such as "Creating the archive failed. File 'hello.d' 
could not be read because you do not have read permissions to it." with 
options "Retry as Administrator", "Exclude 'hello.d'" or "Cancel". 
Knowing programatically what has gone wrong is important in many cases.


> The fact that the coder doesn't need to think hard to use enforce() 
> effectively is a plus, not a minus. An overdesigned enforce that adds 
> extra burden to its user would have been a mistake.

That's indeed true. Throwing an Exception with no info is still better 
than not throwing at all, and creating useful exceptions isn't always 
easy, nor economically rewarding. What's important is to make it easy 
to improve the thrown exception when it becomes relevant. For instance

	// first version: throws standard exception
	enforce(1 == 1, "access denied to " ~ filename);

	// refined version: throws custom exception
	enforce(1 == 1, new FileException("access denied to " ~ filename, 
accessDeniedError, filename));


-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/



More information about the Digitalmars-d mailing list