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