Feature #16260
closedSymbol#to_proc behaves like lambda, but doesn't aknowledge it
Description
Seems that Symbol#to_proc returns Proc that has lambda semantics:
proc = :+.to_proc
proc.call(1, 2) # => 3
proc.call([1, 2]) # ArgumentError (wrong number of arguments (given 0, expected 1))
But if you ask...
That seems to be an inconsistency, which I'd like to clarify. There are obviously two ways to fix it:
- Make it respond
truetolambda?(and mention the semantics in docs) - Make it behave like non-lambda.
The second one seems to produce some useful behavior:
# Currently:
[1, 2].zip([3, 4]).map(&:+) # ArgumentError (wrong number of arguments (given 0, expected 1))
# With non-lambda:
class Symbol
def to_proc
proc { |o, *a| o.send(self, *a) }
end
end
[1, 2].zip([3, 4]).map(&:+) # => [4, 6]
Probably all of it was discussed when Symbol#to_proc was introduced, but as old NEWS-files doesn't link to tickets/discussions, I can't find the reasoning for current behavior.
Updated by Eregon (Benoit Daloze) over 6 years ago
I think we should just return true for lambda?.
Proc has extra confusing behavior, e.g., #16166.
Updated by nobu (Nobuyoshi Nakada) over 6 years ago
- Status changed from Open to Rejected
As a symbol proc cannot know the method to be invoked, so now I think it cannot be lambda.
In the case :+, it looks like a lambda, but it is not always true.
Updated by mame (Yusuke Endoh) over 6 years ago
Just curious: How do you want to use the result of lambda?? Even if it returns true, we may pass an arbitrary number of arguments: lambda {|*a| ... }. I think that lambda? is useless except debugging.
Updated by zverok (Victor Shepelev) over 6 years ago
As a symbol proc cannot know the method to be invoked, so now I think it cannot be lambda.
In the case:+, it looks like a lambda, but it is not always true.
@nobu (Nobuyoshi Nakada), I am not sure I get it right. Can you please show when it is not true?..
For as far as I can understand, there are two distinctions of lambda:
- Its
returnreturns from lambda itself, not enclosing scope - It treats parameters strictly, without implicit unpacking/optionality
Now, :+.to_proc behaves this way:
PLUS = :+.to_proc
PLUS.call(1, 2)
# => 3
PLUS.call([1, 2])
# ArgumentError (wrong number of arguments (given 0, expected 1))
# Tried to call [1, 2].+(), not 1.+(2), so no unpacking
Whilst lambda would behave this way:
PLUS_L = lambda { |obj, *rest| obj.send(:+, *rest) }
PLUS_L.call(1, 2)
# => 3
PLUS_L.call([1, 2])
# ArgumentError (wrong number of arguments (given 0, expected 1))
# Explicit return:
lambda { |obj, *rest| return obj.send(:+, *rest) }.call(1, 2)
# => 3
....and proc will behave this way:
PLUS_P = lambda { |obj, *rest| obj.send(:+, *rest) }
PLUS_P.call(1, 2)
# => 3
PLUS_P.call([1, 2])
# => 3
# Implicit unpacking
# Explicit return:
proc { |obj, *rest| return obj.send(:+, *rest) }.call(1, 2)
# --- returns from the enclosing scope
So, :<sym>.to_proc behaves exactly like lambda, and nothing like proc.
The only thing that differs from the equivalent lambda is...
(which is ideally to be fixed too, as in fact the first parameter is indeed mandatory.)
Can you please show me the case when :<sym>.to_proc does NOT behave like lambda?..
Just curious: How do you want to use the result of
lambda??
@mame (Yusuke Endoh) For explanatory and educational purposes, at least. For example, in this article, I am showing some funny examples, and to explain why this works:
...and this not:
...I'd like to just say "because :+.to_proc is a lambda, as you can see", but what I really need to say is "becuase :+.to_proc doesn't unpacks arguments, behaving like lambda... though it doesn't aknowledge it is"".
So, yep, debugging, explaining, teaching, this kind of things.
Updated by Eregon (Benoit Daloze) over 6 years ago
- Status changed from Rejected to Open
I agree with @zverok (Victor Shepelev) here, a method behaves as a lambda, and doesn't unpack arguments (except a few special methods that specifically do that).
@nobu (Nobuyoshi Nakada) I think we should merge your PR. Could you show an example of a Symbol#to_proc Proc that behaves like a proc and not a lambda? I think that's only rare exceptions (due to that method semantic, not due to the generated Proc), and so Symbol#to_proc should acknowledge it's a lambda.
Updated by mame (Yusuke Endoh) over 6 years ago
- Tracker changed from Misc to Feature
- Assignee set to nobu (Nobuyoshi Nakada)
- Target version set to 36
At the previous meeting, matz said it should return true. Will do.
Updated by nobu (Nobuyoshi Nakada) over 6 years ago
- Status changed from Open to Closed
Applied in changeset git|f0b815dc670b61eba1daaa67a8613ac431d32b16.
Proc made by Symbol#to_proc should be a lambda [Bug #16260]
Updated by hsbt (Hiroshi SHIBATA) almost 6 years ago
- Target version changed from 36 to 3.0