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...
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 
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) almost 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) almost 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) almost 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) almost 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...
PLUS.parameters # => [[:rest]]
PLUS_L.parameters # => [[:req, :obj], [:rest, :rest]]
(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.
        
          
          Updated by Eregon (Benoit Daloze) almost 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) almost 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 5 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) about 5 years ago
          
          
        
        
      
      - Target version changed from 36 to 3.0