Issue #16499 has been updated by matz (Yukihiro Matsumoto).

Status changed from Open to Rejected

There could be enormous code breakages by the proposed change. The compatibility is more important than slightly better consistency.

Matz.


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

* Author: Eregon (Benoit Daloze)
* Status: Rejected
* 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>