June 16, 2010
On 2010-06-16 10:53:12 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@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@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@michelf.com
http://michelf.com/

June 16, 2010
Michel Fortin wrote:
> On 2010-06-16 05:15:24 -0400, Walter Bright <newshound1@digitalmars.com> said:
> 
>> 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.
> 
> True.
> 
> Yet, enforce is inside std.contracts. If that isn't conflating the two concepts I wonder what it is. :-)

I agree completely. enforce must move.
June 16, 2010
Ary Borenszweig wrote:
> On 06/16/2010 04:15 PM, Walter Bright wrote:
>> 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.

It has nothing to do with being dumb, as it is not obvious.

Contracts are for verifying that your program is in a state that it is designed to be in. A contract failure is defined as a program bug.

Errors, on the other hand, are things that can go wrong at run time, like your disk is full when trying to write a file. These are NOT program bugs.

Another way to look at it is your program should continue to operate correctly if all the contracts are removed. This is not true of removing all error checking and handling.

Furthermore, errors are something a program can recover from and continue operating. Contract failures are ALWAYS fatal. A common newbie (and some expert) misconception is that contract failures can or even must be recovered. This comes from a misunderstanding of the basic principles of engineering a safe and reliable system.
June 16, 2010
Walter Bright wrote:
> Michel Fortin wrote:
>> On 2010-06-16 05:15:24 -0400, Walter Bright <newshound1@digitalmars.com> said:
>>
>>> 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.
>>
>> True.
>>
>> Yet, enforce is inside std.contracts. If that isn't conflating the two concepts I wonder what it is. :-)
> 
> I agree completely. enforce must move.

Where to?

Andrei
June 16, 2010
Andrei Alexandrescu, el 16 de junio a las 07:53 me escribiste:
> >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.

Exception hierarchy is only one way to discriminate error types. Extra info, is another (like an error code). I agree that a *large* exception hierarchy hurts more than it helps.

> >>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.

I think Python has a good one. I find myself discriminating between ValueError, IndexError, KeyError, OSError and IOError all the time.

> 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 is, if you don't care on handling errors and let the program crash with a backtrace, or add a big try {} catch (Exception) in the main. If that's not the case, it only produce a false feeling that D (standard library) is good handling errors when it's not, it's just a binary "there is an error" - "there is no errors".

-- 
Leandro Lucarella (AKA luca)                     http://llucax.com.ar/
----------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------
The average person laughs 13 times a day
June 16, 2010
Lutger 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'd think of it this way: enforce() is part of defensive programming, and contracts are related to software testing.

That's probably a pretty good way of putting it. It's essentially the difference between when you use assertions and when you use exceptions. Assertions assert that something is _always_ true and that if it isn't, the program is wrong, while exceptions are for exceptional circumstances (as opposed to _never_) and indicate an error of some kind which is likely outside the control of the program - such as something happening with the file system, user input, or the amount of available memory.

enforce() appears to effectively be the exception equivalent to assert(). You use it when you want an exception thrown rather than when you want to kill your program due to an error. Unfortunately, the difference between when assertions should be used and when exceptions should be used is one that is just subtle enough that it often trips people up, even though in theory it should be fairly straightforward.

- Jonathan M Davis
June 16, 2010
Andrei Alexandrescu wrote:

>> Anyway, I think enforce() is poisson,
> 
> Indeed it is a bit fishy :o).

LOL. My thoughts exactly.
> 
>> 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.
> 
>>> 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 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.
> 
> 
> Andrei

I think that exception hierarchies can be quite useful, but in most cases, I haven't seen projects bother with them. I do think that certain types of exceptions can be useful as separate types as long as they inherit from the base exception type and you therefore don't _have_ to worry about the hierarchy.

A good example of a useful exception type IMO is Java's IOException. It makes good sense to handle them in a specific way separate from general exceptions. You can frequently recover just fine from them, and it allows you to handle I/O-related exceptions gracefully while other exceptions might be considered fatal. However, those other exceptions - especially those which are from more or less unrecoverable errors NullPointerExceptions or OutOfMemoryExceptions - don't necessarily gain much from an exception hierarchy. So, I think that it really depends on the exception.

I do think, however, that there are certain types of exceptions which can benefit from having their own type because it allows you to handle them in a specific manner separate from general and/or unrecoverable exceptions.

- Jonathan M Davis
June 16, 2010
Andrei Alexandrescu wrote:

> Walter Bright wrote:
>> Michel Fortin wrote:
>>> On 2010-06-16 05:15:24 -0400, Walter Bright <newshound1@digitalmars.com> said:
>>>
>>>> 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.
>>>
>>> True.
>>>
>>> Yet, enforce is inside std.contracts. If that isn't conflating the two concepts I wonder what it is. :-)
>> 
>> I agree completely. enforce must move.
> 
> Where to?
> 
> Andrei

I would point out that pretty much nothing in std.contracts actually relates to contracts. Rather, it relates to error handling. So, it would probably be a good idea to simply rename the module - perhaps to std.error.

- Jonathan M Davis
June 16, 2010
Andrei Alexandrescu wrote:
> Walter Bright wrote:
>> Michel Fortin wrote:
>>> On 2010-06-16 05:15:24 -0400, Walter Bright <newshound1@digitalmars.com> said:
>>>
>>>> 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.
>>>
>>> True.
>>>
>>> Yet, enforce is inside std.contracts. If that isn't conflating the two concepts I wonder what it is. :-)
>>
>> I agree completely. enforce must move.
> 
> Where to?

Dunno.
June 16, 2010
Walter Bright wrote:
> Andrei Alexandrescu wrote:
>> Walter Bright wrote:
>>> Michel Fortin wrote:
>>>> On 2010-06-16 05:15:24 -0400, Walter Bright <newshound1@digitalmars.com> said:
>>>>
>>>>> 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.
>>>>
>>>> True.
>>>>
>>>> Yet, enforce is inside std.contracts. If that isn't conflating the two concepts I wonder what it is. :-)
>>>
>>> I agree completely. enforce must move.
>>
>> Where to?
> 
> Dunno.

import std.dunno;
Works for me.