Issue #16499 has been updated by Eregon (Benoit Daloze).


zverok (Victor Shepelev) wrote:
> I believe curent behavior is pretty consistent, as it describes what it would realy accept.

Yes, in that regard it's inconsistent.
It might be impractical though, depending on whether you want something that reflects what the user writes (i.e., I'd argue always unexpected for the user to be called with 0 arguments) or how many arguments the method accepts.

But anyway `Proc#arity` is clearly inconsistent with `parameters`:
```ruby
> proc { |a,b=1| }.arity
=> 1 # => should be -1, accepts any amount of arguments
> lambda { |a,b=1| }.arity
=> -2

In contrast to:
> proc { |*rest| }.arity
=> -1 # OK
> lambda { |*rest| }.arity
=> -1 # OK
```

And I'd argue `Proc#arity` is what should be used to know how many arguments are required and allowed, not `Proc#parameters`.

----------------------------------------
Feature #16499: define_method(non_lambda) should not the semantics of the given Proc
https://bugs.ruby-lang.org/issues/16499#change-83799

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
From https://bugs.ruby-lang.org/issues/15973?next_issue_id=15948&prev_issue_id=15975#note-38

But I think we should change `define_method(&non_lambda)` because that currently confusingly treats the same block body differently (e.g., the same `return` in the code means something different).

This is the only construct in Ruby that can change a non-lambda to a lambda, and it's very inconsistent.
It also forces implementations to have a way to convert a proc to a lambda, which is a non-trivial change.

We could maybe make `define_method(name, non_lambda)` just wrap the Proc in a lambda, automatically,
just like we can do manually with: `define_method(name, -> *args { non_lambda.call(*args) })`.
But it would also preserve `arity`, `parameters`, etc.
Then it wouldn't be any more verbose, but it would avoid the problem of treating the same `return`/`break` in the code differently.

My point is we shall never change the semantics of `return`/`break` somewhere in the code.
It should always mean exactly one thing.
`define_method(name) { literal block }` is fine with that rule, it always behave as a lambda.
But `define_method(&non_lambda)` is problematic as `non_lambda` can be passed to other methods or called directly.

I believe exactly 0 people want `foo { return 42 }` to change its meaning based on whether `foo` calls `define_method` or not.

OTOH, it seems people have repeatedly wanted to convert a proc to a lambda, but for other reasons.
We should look at those reasons and provide better alternatives.

I think sometimes people want to know how many arguments a non-lambda Proc takes.
For example, `proc { |a,b=1| }`.
`proc.arity` gives `1` here which might be helpful but also surprising as that Proc accepts any number of arguments.
They might also look at `proc.parameters` which gives `[[:opt, :a], [:opt, :b]]` which does not differentiate `a` and `b` even though only `b` has a proper default value.
`lambda { |a,b=1| }.parameters` returns the more useful `[[:req, :a], [:opt, :b]]`.

Maybe we should return the same as for a lambda for `non_lambda.parameters`?
`Proc#lambda?` would still tell whether it's strict about arguments and whether it deconstructs them.

cc @zverok



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>