Re: How to short-circuit match failure?
- To: mathgroup at smc.vnet.net
- Subject: [mg114370] Re: How to short-circuit match failure?
- From: Leonid Shifrin <lshifr at gmail.com>
- Date: Thu, 2 Dec 2010 05:40:39 -0500 (EST)
Hi kj,
A nice solution! It offers a good way of separating error-checking code from the
actual one (so that in principle, error - checking code could be generated
programmatically - one just has to parse the declarations (patterns) of the
actual
code).
One small remark: you don't actually have to use the argsok variable,
due to the way conditional definitions work (core[args] is only evaluated if
chkargs[args], which is evaluated first, gives true). So, this will work
just as well:
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, core},
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;
core[args] /; chkargs[args]]
Regards,
Leonid
On Wed, Dec 1, 2010 at 10:14 AM, kj <no.email at please.post> wrote:
>
> 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
>
>