Charles Greathouse on Fri, 15 Feb 2013 19:37:34 +0100


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: PARI 2.6 syntax 1: iferr/iferrname


> I am not sure that two separate functions iferr / iferrname are useful.

I agree, and I think it's probably a bit confusing for people learning the language.

I like the syntax

iferr("error_name"  /* (1) */
    , /* code that may raise an exception (2) */
    , /* variable E containing the error, optionally some_predicate(E) (3) */
    , /* recovery code (4) */ )

though I think you could be even more ruthless: (1), (3), and (4) could be optional. If (1) is omitted any error is trapped; if (3) is omitted the error can't be accessed; if (4) is omitted the code simply breaks out of the iferr on an error.

Charles Greathouse
Analyst/Programmer
Case Western Reserve University


On Fri, Feb 15, 2013 at 12:32 PM, Karim Belabas <Karim.Belabas@math.u-bordeaux1.fr> wrote:
* Bill Allombert [2013-02-14 22:42]:
[...]
> This is an example of the iferr interface:
>
> invmod(a,N)=
> {
>   iferr(Mod(b,N)^-1
>       ,E, my(T);
>        if(errname(E)=="e_INV"&&type(T=component(E,2))=="t_INTMOD",
>          return([gcd(lift(T),N)])
>         ,error(E)));
> }
>
> iferrname allows to simplify a bit:
>
> invmod(a,N)=
> {
>   iferrname("e_INV"
>       ,Mod(b,N)^-1
>       ,E ,my(T);
>         if(type(T=component(E,2))=="t_INTMOD",
>           return([gcd(lift(T),N)])
>          ,error(E)));
> }
>
> but the imbricated 'if' seems clumsy.
> Maybe we could allow:
>
>   my(T);
>   iferrname("e_INV", Mod(b,N)^-1
>       ,E
>       ,type(T=component(E,2))=="t_INTMOD"
>       ,return([gcd(lift(T),N)]))
>
> Better idea ?

I am not sure that two separate functions iferr / iferrname are useful.
We could have a single iferr() -- corresponding to current iferrname --
with the following syntax:

  iferr("error_name"  /* (1) */
    , /* code that may raise an exception (2) */
    , /* variable E containing the error, optionnally some_predicate(E) (3) */
    , /* recovery code (4) */ )

(1) "error_name" is optional. Omitting it results in current iferr()
behaviour: trap all runtime exceptions. Otherwise only trap exceptions
with the expected name.

(2) is straightforward

(3) must contain a variable name, say E, to store the exception context
for later manipulations.

I like the idea of being able to ascertain that the exception is the one
we intended to catch for complicated cases (unlike your toy example
above where the "not invertible" exception can only come from a
t_INTMOD, making the test spurious)

But it's not that nice to impose an extra argument in the frequent case
where the code is simple enough to *know* that an exception with the
expected name can only come from the "right" error. I see two
possibilities:

 * sol. 1: "binding" the predicate to the error E: allow either

   - a variable name, says 'E' by itself (current syntax)

   - or something like

     E | some_predicate(E)

   [ to be read "E such that..." ] Probably impossible without
   introducing yet another argument code. You tell us :-)

 * sol. 2: having two functions, the simple iferr(), with just a variable name,
   and a more complicated iferrtest(), say, with an extra argument
   corresponding to a predicate E must satisfy in order for us to catch
   this particular exception. This seems very easy to implement, without
   introducing a new argument code.

(4) is relatively straightforward. Non-obvious part: it can use
component(E,i)  [ or Vec(E) ] to access the exception context and
recover in a sensible way. Such as returning an integer factor in your
toy example.


Independently of the above, it would be "nice" to support a
simplified form in the case where the exception context 'E' is not
needed at all (no predicate on E, and E unused in the recovery code):

  iferr("error_name"  /* (1) */
    , /* code that may raise an exception (2) */
    , /* recovery code (4) */ )

(only 3 arguments). I am not sure whether this is a good idea [ have the
argument semantic depend on the total number of arguments received by the
function ], and it only saves 2 or 3 keystrokes :-)

>   iferrname("e_DOMAIN",
>     exp(-tan(x)^2)
>    ,E
>    ,if(component(E,1)=="tan" && component(E,4)=="Pi/2 + kPi"
>      ,0
>      ,error(E)))

sol. 1:

  iferr("e_DOMAIN", exp(-tan(x)^2)
    , E | my([f,v,op,lim,x] = Vec(E)); f=="tan" && lim=="Pi/2 + kPi"
    , 0);

sol. 2:

  iferrtest("e_DOMAIN", exp(-tan(x)^2)
    , E, my([f,v,op,lim,x] = Vec(E)); f=="tan" && lim=="Pi/2 + kPi"
    , 0);

1) see ??e_DOMAIN@ for an explanation of the [f,v,op,lim,x] notations :-)

2) no need to rethrow E at the end since if some_predicate(E) does not
evaluate to "true" (non-zero), we don't catch E at all.


Cheers,

    K.B.
--
Karim Belabas, IMB (UMR 5251)  Tel: (+33) (0)5 40 00 26 17
Universite Bordeaux 1          Fax: (+33) (0)5 40 00 69 50
351, cours de la Liberation    http://www.math.u-bordeaux1.fr/~belabas/
F-33405
Talence (France)       http://pari.math.u-bordeaux1.fr/  [PARI/GP]
`