The thx.error package contains a bunch of classes to manage errors in your applications. The base class is Error and is a lightweight container for error messages. The nice thing about it is that it separates the message itself from its parameters allowing to format it in a meaningful way:
var error = new Error("invalid import {0:C}", 0.123);
trace(error);
// trace results is: invalid import $0.12
If you provide the needed infrastructure you can also use an instance of ITranslation to deliver your messages:
// Translated Error
var translator = new DictionaryTranslation(ItIT.culture);
translator.addSingular("invalid import {0:C}", "importo invalido {0:C}");
trace(error.translate(translator));
// trace result is: importo invalido € 0,12
I often use to mark my abstract methods so that at least at runtime I have a feedback that the implementation is missing and I do that using the AbstractMethod error class (the NotImplemented error class is functionally equivalent but has a different semantic).
// AbstractMethod / NotImplemented
// in a real world context this will be an instance method and not an inline function
function notImplemented() throw new NotImplemented();
try
{
notImplemented();
} catch (e : Dynamic) {
trace(e);
}
The nice thing is that I don’t have to type any message into the constructor because all the info I need are already in there; this is the trace:
method Main.notImplemented() needs to be implemented
Finally the NullArgument error class has a couple of static constructors that helps me check for null arguments. Using macros the argument name is extracted so that you don’t need to type it as a string; that spares you some typing but better yet avoids annoying and error prone repetitions.
// NullArgument
function toUpperCase(text : String)
{
NullArgument.throwIfNull(text);
return text.toUpperCase();
}
try
{
toUpperCase(null);
} catch(e : Dynamic)
{
trace(e);
}
// trace result is: invalid null argument 'text' for method Main.toUpperCase()
The NullArgument.throwIfNullOrEmpty works similarly but also test for emptiness … this works on Strings, Lists, Arrays (length) and objects (no fields). Note that the type discovery is made at compile time and not at runtime:
// null or empty function pop(arr : Array) : String { NullArgument.throwIfNullOrEmpty(arr); return arr.pop(); } try { pop([]); } catch(e : Dynamic) { trace(e); } // trace result is: invalid null or empty argument 'arr' for method Main.pop()
Very nice post, Franco!
I like the way you handle error translations. My own preference for Errors, though, is to define them all as enums, so we can kind of strictly type all possible errors a library / code part can throw. When I’m using them, sometimes I have a try with multiple different catches – one for each application part. The cool part about that is that you can add another error and get compilation errors if you forget to handle this type of error.