Search code examples
rubylambdablockampersandruby-1.9.3

Kernel.lambda() not returning a lambda Proc in Ruby 1.9


I'm writing a simple event bus to use in an application. This part works fine:

#!/usr/bin/env ruby

module EventBus
  @callbacks = {}

  module_function

  def on event, &callback
    @callbacks[event] ||= []
    @callbacks[event].push callback
  end

  def fire event, *data
    @callbacks[event].each do |callback|
      callback.call *data
    end
  end
end

EventBus.on :foo do |x| puts x end
EventBus.fire :foo, "test"
# => test

Because of the complex nature of my program, and the fact that Procs take arguments in a very loosey-goosey fashion, I want some argument checking on my events. lambda is the obvious choice for this:

EventBus.on :bar, &(lambda do |x| puts x end)

# Will raise an ArgumentError, since the event was fired without any arguments
# Remember that this is the desired behavior
EventBus.fire :bar

Clearly, the syntax of the on call is ugly due to the &(lambda do ... end). I'd rather be able to just use do ... end (i.e. just pass it a normal block without the unary ampersand operator) and convert that into a lambda. I tried the obvious:

...
  def on_lambda event, &callback
    @callbacks[event] ||= []
    @callbacks[event].push(lambda &callback)
    # check if the added callback is lambda. (spoiler alert: it isn't)
    puts @callbacks[event].last.lambda?
  end
end

EventBus.on_lambda :baz do |x| puts x end
# I would expect the callback to be a lambda, and thus throw an ArgumentError,
# but neither of these holds.
EventBus.fire :baz

As I understand it, on_lambda takes a block, converts it to a Proc referenced locally by callback (via the &). I'm pushing the result of calling lambda with callback converted back in to a block. I expect this to return a lambda derived from the callback Proc, so my question is: why is the element on the array a normal Proc.?

ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]


Update: Prompted by megas's mention of { } I looked into the ->() { } syntax (I've seen it called the "stab" operator). It produces a lambda from the block specified. It's probably as close as I'll get to the mixture of normal block syntax and argument checking I wanted. All I have to do is tweak the on method:

def on event, callback
  @callbacks[event] ||= []
  @callbacks[event].push callback
end

and then attach callbacks like so:

EventBus.on :bam, ->(x) do
  puts x
end

I'm still curious as to the original question, however.


Solution

  • I would suggest reading this: http://ruby-doc.org/core-1.9.3/Proc.html#method-i-lambda-3F