Seems that Symbol#to_proc returns Proc that has lambda semantics:
proc=:+.to_procproc.call(1,2)# => 3proc.call([1,2])# ArgumentError (wrong number of arguments (given 0, expected 1))
But if you ask...
proc.lambda?# => false
That seems to be an inconsistency, which I'd like to clarify. There are obviously two ways to fix it:
Make it respond true to lambda? (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:classSymboldefto_procproc{|o,*a|o.send(self,*a)}endend[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.
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.
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.
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 return returns from lambda itself, not enclosing scope
It treats parameters strictly, without implicit unpacking/optionality
Now, :+.to_proc behaves this way:
PLUS=:+.to_procPLUS.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|returnobj.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|returnobj.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:
[1,2,3].zip([4,4,4]).map{|a,b|a+b}
...and this not:
[1,2,3].zip([4,4,4]).map(&:+)
...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.
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.