mauke / Function-Parameters

Function::Parameters - define functions and methods with parameter lists ("subroutine signatures")
https://metacpan.org/pod/Function::Parameters
18 stars 19 forks source link

Is there any configuration setting to restore optional parameter lists in v2? #28

Closed sdt closed 7 years ago

sdt commented 7 years ago

Hello,

We use Function::Parameters quite extensively at $work and have lots of instances where we use the method keyword without a parameter list.

eg.

has something => (
   is => 'lazy',
   builder => method { return $self->foo },
);

This code all breaks with version 2.

Is there some sort of configuration option we can use to make that syntax work again?

Thanks

mauke commented 7 years ago

No, sorry. I removed that syntax because I thought the difference between method { and method () { was too subtle.

You could run perl -i.bak -pe 's/\bmethod\s*\{/method (\@) {/' over all files to get a similar effect, but I don't know how practical that is for you.

sdt commented 7 years ago

No chance of restoring the old syntax or adding it as an option?

Seems like a reasonable default case.

veryrusty commented 7 years ago

One of the reasons I've been using Function::Parameters is that is makes a codebase much easier to read; by both experienced developers and those new to perl. method {...} isn't much different to perl sub {} syntax and is easily interpreted as being "this method takes no params".

Whereas method (@) {...} is very different; a bracketed sigal isn't common[1] perl syntax. Its worse when its doing double duty for "any number or none".

Is there any possibility of allowing method {...} syntax and throwing an error if there was more than $self in @_ ?

[1] I don't consider subroutine prototypes common perl syntax.

mauke commented 7 years ago

I really don't like the idea of restoring the old syntax. It would probably be more effort and take me longer than adding (@) to your whole code base.

About making an absent parameter list equivalent to an empty one: I like that idea better but I have to think about it.

The (@) syntax isn't a special case. It's just an unnamed slurpy parameter; the core "signatures" syntax is identical.

I don't recommend using (@); that's just a quick and dirty fix for old code that didn't specify a parameter list and where it's not easily possible to check what every single caller passes in. For new code you should think about your parameters and write a more specific parameter list that only accepts what you need.

sdt commented 7 years ago

I might have been asking for something different here.

I've been using these cases:

method invocant_with_no_params { ... } method invocant_with_slurpy_params(@args) { ... }

So I take it the only change I really need is to add an empty params list to the first case:

method invocant_with_no_params() { ... }

Is that correct?

mauke commented 7 years ago

Yes, that's correct. If your method takes no arguments, just write ().

(But you might discover that you have some callers that do pass arguments because in Function::Parameters 1.x the lack of a parameter list was silently interpreted as "anything goes".)

sdt commented 7 years ago

No worries. Thanks for Function::Parameters by the way. I've been using it for a few years now and it works a treat.

mauke commented 7 years ago

To summarize: Should omitting a parameter list be allowed and if so, what should it do? There are a few options:

  1. It should mean the same thing as sub { ... }: All arguments allowed but ignored (apart from being available in @_).
  2. It should mean the same thing as (): No arguments allowed.
  3. It should be an error.

Going with (2) seems more logical than (1), especially given the shift towards stricter argument checks in Function::Parameters. Option (1) suffers from the problem that method foo() { ... } looks really similar to method foo { ... } but means something very different, and what it means is not something you need very often (or at least I'm trying to discourage it) so it shouldn't get the shortest syntax. I might choose (2) if I didn't have to worry about backwards compatibility: Version 1 of Function::Parameters chose (1), so a switch to (2) would silently change the meaning of existing code in a way that causes runtime errors when certain (parameter-list-less) functions are called in a certain way (passing arguments).

This is why I think (3) is the best choice in this situation: