Project

General

Profile

Feature #16499

define_method(non_lambda) should not change the semantics of the given Proc

Added by Eregon (Benoit Daloze) 3 months ago. Updated 2 months ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:96768]

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.

cc zverok (Victor Shepelev)


Related issues

Related to Ruby master - Feature #15973: Let Kernel#lambda always return a lambdaAssignedmatz (Yukihiro Matsumoto)Actions
Related to Ruby master - Feature #15357: Proc#parameters returns incomplete type informationOpenActions

Also available in: Atom PDF