Feature #16499
closeddefine_method(non_lambda) should not change the semantics of the given Proc
Description
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.