Re: How to short-circuit match failure?
- To: mathgroup at smc.vnet.net
- Subject: [mg114344] Re: How to short-circuit match failure?
- From: kj <no.email at please.post>
- Date: Wed, 1 Dec 2010 02:14:38 -0500 (EST)
- References: <ictfq8$mck$1@smc.vnet.net> <id01ld$h6o$1@smc.vnet.net>
Thanks for all your comments and suggestions. Inspired by them, I came up with this general approach for defining a function that emits diagnostic messages when called with incorrect arguments, and returns the original expression (unevaluated). The idea is to replace something like this: ClearAll[foo] foo::toolong = "First argument is too long"; foo::nolist = "First argument is not a list"; foo::nargs = "foo called with `1` argument(s); 2 expected"; foo[x_List /; Length[x] < 3, y_] := {#, y} & /@ x foo[_List, _] /; Message[foo::toolong] = Null foo[_, _] /; Message[foo::nolist] = Null foo[x___] /; Message[foo::nargs, Length[{x}]] = Null with something like this: ClearAll[foo] foo::toolong = "First argument is too long"; foo::nolist = "First argument is not a list"; foo::nargs = "foo called with `1` argument(s); 2 expected"; foo[args___] := Module[{chkargs, argsok, core, othervars}, chkargs[x_List /; Length[x] < 3, y_] := True; chkargs[_List, _] := Message[foo::toolong]; chkargs[_, _] := Message[foo::nolist]; chkargs[x___] := Message[foo::nargs, Length[{x}]]; core[x_, y_] := {#, y} & /@ x; argsok = chkargs[args]; argsok && core[args] /; argsok ] The function is defined as a *single* (delayed) rule that evaluates to a module with a standard structure. The key points are these: 1. the rule has a single catchall pattern (args___), and therefore it will apply to all invocations of the function; 2. all the argument-checking is transferred basically verbatim to a module-local rule called checkargs; this rule evaluates to True whenever the arguments are valid, or to a call to Message with the appropriate diagnostic; this terminates the argument-checking, so any remaining chkargs rules are skipped; 3. the function's main action (which assumes all the arguments are ok) is defined as one or more module-local delayed rules called core; since all the argument matching and checking is delegated to the chkargs rule(s), the pattern(s) for the core rule(s) can be more general (i.e. simpler) than would be required otherwise (e.g. in the example above, the pattern is simply x_, y_); 4. the last two lines of the module are standard (always the same); in the next-to-last line, argument checking is done by evaluating chkargs[args} and assigning the reuslt to the module-local variable argsok; in the process, diagnostic messages are emitted if the checking fails; in this case, argsok will have value Null; otherwise, it will have value True; 5. in the last line, if argsok is True, core[args] is evaluated, and returned as the module's evaluation value; if argsok is not True (i.e. it is Null), core[args] is not evaluated, and furthermore the main module's evaluation will fail thanks to the failed /; argsok condition at the end, so the final evaluation will yield the original expression, unevaluated; 6. one feature not illustrated by the simple example above is that the DownValues of the chkargs rules can be more elaborate, and even set the process of variables local to the main module (represented by othervars in the example); these variables can be shared with other chkargs rules, or core rules, and thus avoid having to perform some calculations needed by several of them more than once. This approach seems pretty general to me, and it is basically a wrapper around the "standard" approach. As always, your comments and criticisms are always welcome. ~kj