Project

General

Profile

Actions

Feature #9777

closed

Feature Proposal: Proc#to_lambda

Added by schneems (Richard Schneeman) over 10 years ago. Updated almost 7 years ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:62175]

Description

Currently different block objects such as a lambda can be converted into to a proc: http://www.ruby-doc.org/core-1.9.3/Proc.html#method-i-to_proc

However you cannot turn a Proc instance into a lambda. Since a Proc and lambda behave differently sometimes you may want to convert between the two functionalities. One example is a return inside of the block. In a lambda the return keyword exits the closure, in a Proc the return keyword raises an exception.

There is currently no implementation standard way to convert a Proc to a lambda. I made a gem that makes this easier: https://github.com/schneems/proc_to_lambda but it seems overkill.

If MRI introduces a to_lambda method on Proc then we can standardize on an interface for this behavior. This question on stack overflow has been upvoted many times: http://stackoverflow.com/questions/2946603/ruby-convert-proc-to-lambda. I think other Ruby developers would like this behavior supported by Ruby core.


Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #15973: Let Kernel#lambda always return a lambdaClosedmatz (Yukihiro Matsumoto)Actions

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

  • Description updated (diff)

Richard Schneeman wrote:

Currently different block objects such as a lambda can be converted into to a proc: http://www.ruby-doc.org/core-1.9.3/Proc.html#method-i-to_proc

It changes nothing, but returns self as-is, without any effects.

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

  • Status changed from Open to Feedback

Why can't you use break and next?

Updated by schneems (Richard Schneeman) over 10 years ago

Sorry for the delayed response. I was not "watching" this issue by default.

Why can't you use break and next?

I wanted this to make a public api based on anonymous functions: https://github.com/opro/opro#password-token-exchange (note the return used in this section towards the bottom). New developers know the semantics of return very well but the differences between lambda and Proc are confusing to them, they are not familiar with the break keyword. If i tell new developers to use this API, create a block, and use break many would not do so and still use return by accident. This is my use-case, but others may have different or better ones. As mentioned 16 people were interested in this enough to google it, find the stack overflow post and up-vote the answer.

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

return in a proc exits the defined method, unless it has returned already.
Turning a proc into a lambda disappoints that expectation.
It doesn't feel a good idea.

Updated by avit (Andrew Vit) over 10 years ago

Would it work to just wrap it inside a lambda to get the semantics you want?

Updated by schneems (Richard Schneeman) over 10 years ago

Andrew Vit wrote:

Would it work to just wrap it inside a lambda to get the semantics you want?

Like lambda &proc? That would be fine. As Nobu mentioned Proc and lambda behave differently, sometimes I want control over the behavior of my program so I want the ability to change the object I am using from proc to lambda or lambda to proc.

When I made the original request I did not know that lambda#to_proc was basically a no-op. I thought it actually changed the behavior.

Updated by avit (Andrew Vit) over 10 years ago

I find that using throw is useful in the situation you describe. It gives a nice explicit message about what's going: it's like a multi-level return to the point where you want to catch it.

Updated by schneems (Richard Schneeman) over 10 years ago

Another possible reason to convert a Proc to a lambda is to for raising error on arguments

foo = -> { puts "hello"  }
foo.call(1)
ArgumentError: wrong number of arguments (1 for 0)
  from (irb):4:in `block in irb_binding'
  from (irb):5:in `call'
  from (irb):5
  from /Users/schneems/.rubies/ruby-2.1.1/bin/irb:11:in `<main>'

bar = Proc.new { puts "hello" }
bar.call(1)
# => "hello"

If a user passes in a proc there will be no errors or warnings that the proc was improperly declared. Converting to a lambda would change the behavior to then raise an error.

Updated by nobu (Nobuyoshi Nakada) over 10 years ago

If a user wanted to write it as a proc, then it means that he/she doesn't want it to raise error, doesn't it?

Updated by kernigh (George Koehler) over 10 years ago

Beware! The answer on Stack Overflow (http://stackoverflow.com/a/2946734) is wrong, because it does not preserve the value of self in the block.

This is better, but still wrong:

def convert_to_lambda &block
  obj = block.binding.eval('self')
  Module.new do
    define_method(:_, &block)
    return instance_method(:_).bind(obj).to_proc
  end
end

It preserves self in the block, but it fails when I pass the lambda to #instance_exec or #module_exec. I will try to post explanation to Stack Overflow.

There is no way to convert proc to lambda, unless Ruby adds a new feature.

I have no reason to support this new feature. I am not wanting libraries to convert my blocks to lambdas. I know how to use next. If someone converts my block to a lambda, then my block can no longer return from the enclosing method, or ignore extra arguments. If I want my block to be a lambda, I can write &->, as in

3.times &->(_) { puts "It works!" }

Updated by myronmarston (Myron Marston) over 10 years ago

We have a need for this feature in RSpec. Specifically, for some of the new features in RSpec 3, we use Method#parameters and Proc#parameters. However, for procs, parameters does not distinguish between args with defaults and args without:

irb(main):001:0> Proc.new { |a| }.parameters
=> [[:opt, :a]]
irb(main):002:0> Proc.new { |a=1| }.parameters
=> [[:opt, :a]]

I understand why, because with procs you can call it without providing the args so all args are optional and it's essentailly like |a=nil|. Lambdas, on the other hand, do distinguish:

irb(main):003:0> lambda { |a| }.parameters
=> [[:req, :a]]
irb(main):004:0> lambda { |a=1| }.parameters
=> [[:opt, :a]]

I attempted to use some code like @schneem's gem to convert a proc to a lambda for the purpose of looking at the parameters (but, importantly, not to call the lambda). Unfortunately, we ran into a ruby bug (#9967) and had to back it out.

It would be nice for an officially supported solution to convert a proc to a lambda. I can understand the issues with calling a converted lambda, but simply using the converted lambda to get more precise info about the block parameters seems more reasonable and safe.

Actions #12

Updated by naruse (Yui NARUSE) almost 7 years ago

  • Target version deleted (2.2.0)
Actions #13

Updated by akr (Akira Tanaka) over 5 years ago

  • Related to Feature #15973: Let Kernel#lambda always return a lambda added
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0