Project

General

Profile

Actions

Feature #16260

closed

Symbol#to_proc behaves like lambda, but doesn't aknowledge it

Feature #16260: Symbol#to_proc behaves like lambda, but doesn't aknowledge it

Added by zverok (Victor Shepelev) about 6 years ago. Updated about 5 years ago.

Status:
Closed
Target version:
[ruby-core:95418]

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:

  1. Make it respond true to lambda? (and mention the semantics in docs)
  2. 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 Actions #1

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 Actions #3 [ruby-core:96323]

  • 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 Actions #4 [ruby-core:96324]

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 Actions #5 [ruby-core:96328]

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:

  1. Its return returns from lambda itself, not enclosing scope
  2. 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 Actions #6 [ruby-core:96329]

  • 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 Actions #7 [ruby-core:96481]

  • 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 Actions #8

  • 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 Actions #9

  • Target version changed from 36 to 3.0
Actions

Also available in: PDF Atom