develooper Front page | perl.perl5.porters | Postings from January 2022

Re: arity checking (was Re: PSC #049 2022-01-07)

Thread Previous | Thread Next
From:
Paul "LeoNerd" Evans
Date:
January 21, 2022 18:28
Subject:
Re: arity checking (was Re: PSC #049 2022-01-07)
Message ID:
20220121182819.18e8e374@shy.leonerd.org.uk
On Fri, 21 Jan 2022 17:49:40 +0000
Dave Mitchell <davem@iabyn.com> wrote:

> If a framework author wants to break a promise about arity, they could
> always call it with an eval:
> 
>     if (defined $extra) {
>         eval { $self->$callback($x, $y, $extra) };
>         goto fallback if $@ =~ /Too many arguments/;
>     }
>     else {
>       fallback:
>         $self->$callback($x, $y);
>     }
> 
> which is as much (or little) hacky as introspecting the callback to
> see if its a sub with a declared arity range.

Weelll - except that it will invoke fallback whenever an exception
matching that string is thrown from *anywhere* inside the callback.
It's not sensitive to just arity checking of $callback itself, but
might accidentally trigger many function-calls nested deeply inside it.

That's quite a hacky solution indeed ;) Lets step back a little.

Signatures work great in a normal "function-call" world wherein the API
shape (the interface definition) is given by the definition of the
function; the callers simply must adhere to it. Here, we put the API
shape on the definition of the function and all works just fine.

The situation it doesn't work in is this callback idea; the places
where the API shape is defined by the caller of some coderef, and
must be adhered to by implementors of those callback functions.

Consider in a statically-typed language, where the API signatures of
all of these things can be given upfront. Perhaps in that world we'd
have a way to define optional vs. required parameters. But as it stands
in Perl we don't. In this callback scenario the caller defines the
interface implicitly by their action. So far we don't have a way for the
caller to say "hey, I'm gonna pass you extra information but I don't
mind if you ignore it".

This was the motivation behind my original suggestion of some pragma
that turns off arity checking from the point of view of the caller:

  {
    no fatal 'arity::max';
    $self->$callback($item, $idx);  # $idx is optional
  }

TEAM on CPAN has provided another hacky solution (this one inspects
optrees for the OP_ARGCHECK op and parses out the fields of it):

  coderef_ignoring_extra($callback)->($self, $item, $idx);

  https://metacpan.org/pod/Acme::Signature::Arity#coderef_ignoring_extra

Of course these solutions aren't very nice, partly because they don't
encode the fact that $item must be handled, but $idx may not be.
Perhaps we can do better?


What would be lovely would be if we could encode a position in a
function call site, to say "from this point onwards, don't worry if the
invoked sub does not handle this argument. They are optional". I wonder
about allowing the keyword `optional` in the middle of a function or
method call argument list:

  $self->$callback($item, optional $idx);

A function/method call with one of these 'optional' words inside it is
then handled slightly differently. If the invoked sub fails to handle
at least all of the arguments before the 'optional' then that's fatal
as it is now, but the caller is saying "I don't mind if the callee
ignores any of the values after here". Thus $callback could be defined
by any of

  sub ($self, $item) { ... }
  sub ($self, $item, $idx) { ... }
  sub ($self, $item, $) { ... }
  sub ($self, $item, @) { ... }

and all would be just fine.

((It would otherwise be a syntax error for 'optional' to appear outside
of a function call argument list, or multiple of them, but that's just
book-keeping)).


This design shape stresses the inversion of the normal design shape of
function calls:

  When dealing with callback scenarios it is the CALLER of the coderef
  who decides the API shape, not the CALLEE. It is therefore up to the
  caller to encode what is optional about it.


Now I'm not hugely fixed on this particular syntax as a solution to the
problem, but I feel that the principle of "caller specifies API" is one
we should explore.

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About