From: zverok.offline@...
Date: 2020-01-12T14:20:06+00:00
Subject: [ruby-core:96802] [Ruby master Feature#16499] define_method(non_lambda) should not the semantics of the given Proc

Issue #16499 has been updated by zverok (Victor Shepelev).


@eregon what is the exact proposal of this ticket? I am not sure neither from title nor from description :(

As a side note, in regards to the last part:

> 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?

I believe curent behavior is pretty consistent, as it describes what it would realy accept. `req` means it will raise "Wrong number of arguments" if the argument is not provided, `opt` means it will accept argument's absence and will provide the default value. So, `proc { |a, b=1|` "real" signature (considering how it will process its args), is in fact `proc { |a=nil, b=1, *|`. **If** some complicated code accepts "any callable" and somehow validates "what args it requires", `opt` is more true for non-lambda's arg than `req`.

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

* 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>