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 > >