Against enforce()

Jonathan M Davis jmdavisProg at gmx.com
Wed Mar 16 17:05:33 PDT 2011


On Wednesday, March 16, 2011 16:45:56 bearophile wrote:
> enforce() seems the part of Phobos that I hate more. Among its faults:
> - It can't be used in weakly pure functions (because you can't use lazy
> arguments in pure functions), and not-purity is viral. Lot of Phobos can't
> be pure because of this, so even more user code can't be pure because of
> this; - It kills inlining (with the current DMD, and I don't think this
> problem will be fixed soon); - I have seen it slow down code (probably
> mostly because of its not-inlining nature);

> - Where it is used it usually
> doesn't give a more meaningful exception like WrongArgumentException, etc.
> I don't want a deep hierarchy of one hundred standard exceptions, but I
> think some standard exceptions for the most common mistakes, like wrong
> arguments, etc, are better than a generic enforce(), especially for a
> standard library code that is meant to be written with care and to give
> better error messages/exceptions.

A number of modules in Phobos have exceptions specific to that module - such as 
DateTimeException or FileException. enforce doesn't stop anyone from using 
specific exceptions. And it's just as easy to use Exception instead of a specific 
exception type when throwing directly, so I don't think that this complaint 
holds much water. It's a valid complaint if Phobos developers are choosing to 
throw Exception instead of more specific exceptions, but that's not enforce's 
fault.

> - It doesn't allow functions to be
> nothrow. This is a fault, because D has Contract Programming, that is
> meant to be usable for nothrow functions too. D Contracts with asserts are
> the right tool.

Overall, I favor exceptions when it comes to testing input rather than 
assertions, but regardless of that. enforce does _not_ stop functions from being 
nothrow. It just makes it more annoying to make them nothrow. If you want the 
caller to be nothrow, you have to have a try-catch block in the caller which 
catches Exception. If the function really isn't ever going to throw, then you 
can use such a try-catch block with an assert(0) in the catch body, and it's 
quite safe. std.datetime does that in several places.

Regardless, your problem here really isn't enforce anyway. Your problem is that 
you think that a number of functions should be using assertions instead of 
exceptions. enforce happens to be a way to throw exceptions, but it's quite easy 
to use exceptions without it. So, enforce doesn't really have anything to do 
with it other than the fact that that was the means used to throw the exception.

> I see enforce() just as a temporary workaround for a problem of Phobos
> (that it's compiled in release mode, so its asserts are vanished) that
> risks to become a permanent part of Phobos.

> So a better solution is for the standard Phobos library to ship in two
> versions, one compiled in release and not release mode, and DMD may choose
> the right one according to the compilation switches. This removes most of
> the need of enforce(). I suggest to deprecate enforce(). Until the problem
> with Phobos compilation is solved and enforces are removed from Phobos,
> enforce() may become a private Phobos function that user code can't
> import.

There a number of places in Phobos which throw exceptions and _should_ throw 
exceptions. ConvExceptions and FileExceptions are great examples. 
DateTimeException is used liberally in std.datetime, and it really wouldn't make 
sense to make them use assertions.

And even in cases where exceptions were chosen of assertions because of the 
release vs non-release mode issue, there's still the question of whether we 
really want Phobos to be throwing AssertErrors, since when a programmer sees an 
assertion which isn't theirs, they're likely to think that it's someone else's 
code which is broken, not theirs. So, having AssertErrors thrown from Phobos 
look just plain bad. Now, in same cases performance still makes assertions more 
desirable - and there _are_ places in Phobos which use exceptions - but there 
are still a lot of situations where Phobos _should_ be throwing exceptions.

Regardless, enforce is supposed to make throwing exceptions similar to asserting 
something. It is - in theory at least - a great idea. The problem with it is its 
laziness and all of the implications that that has (like the inability to inline 
because of it). If we had a non-lazy version of enforce or if the compiler were 
made smart enough to realize that it didn't need a lazy version in some cases 
(such as when it's passed a string literal) and to use a non-lazy version in 
such cases, then the problem would be reduced.

Personally, I rarely use enforce precisely because of the inlining issue. I 
don't that that

if(!condition)
    throw new ExceptionType(msg);

is really much worse than

enforce(condition, new ExceptionType(msg));

but I see no reason to get rid enforce. I also disagree with you about how much 
Phobos should be using exceptions. There _are_ places - such as in a lot of 
range-based functions - which likely need to use assertions rather than 
exceptions for performance reasons, but I do _not_ like the idea of using 
assertions to validate input from code outside of Phobos. I think that that is 
_exactly_ the sort of place that should be using exceptions rather than 
assertions.

- Jonathan M Davis


More information about the Digitalmars-d mailing list