15

The following two scopes generate the same result, which syntax is preferable and is there any other difference?

scope :paid, lambda { |state| where(state: state) }

scope :paid, ->(state) { where(state: state) }
Arif
  • 1,369
  • 14
  • 39

4 Answers4

25

It's preferable, due to readibility reasons, to use new syntax -> (introduced in Ruby 1.9) for single-line blocks and lambda for multi-line blocks. Example:

# single-line
l = ->(a, b) { a + b }
l.call(1, 2)

# multi-line
l = lambda do |a, b|
  tmp = a * 3
  tmp * b / 2
end
l.call(1, 2)

It seems a community convention established in bbatsov/ruby-style-guide.

So, in your case, would be better:

scope :paid, ->(state) { where(state: state) }
markets
  • 6,709
  • 1
  • 38
  • 48
4

-> is literal syntax, like ". Its meaning is fixed by the language specification.

Kernel#lambda is a method just like any other method. It can be overridden, removed, overwritten, monkeypatched, intercepted, …

So, semantically, they are very different.

It is also possible that their performance is different. Kernel#lambda will at least have the overhead of a method call. The fact that the execution engine cannot actually know what Kernel#lambda does at runtime (since it could be monkeypatched) would also preclude any static optimizations, although I don't believe any existing Ruby execution engine statically optimizes lambda literals in any meaningful way.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
2

There is no difference, both returns the same Proc object:

irb(main):033:0> lambda {|x| x*x}
=> #<Proc:0x007ff525b55b90@(irb):33 (lambda)>
irb(main):034:0> ->(x) {x*x}
=> #<Proc:0x007ff525b7e068@(irb):34 (lambda)>

In my opinion, -> is more readable.

Grych
  • 2,861
  • 13
  • 22
0

markets answer is the correct answer. One quick addition though - if a multi-line lambda is ever needed as an arugment, there are 2 approaches that work. For example creating a scope in a model, you may want to consider this:

class User < ApplicationRecord
  # Bad - this will error because we are passing a block as an argument in
  # a method without parenthesis
  scope cool_users lambda do |arg|
    # some long query
  end

  # OK - when parenthesis are added, the lambda block will work 
  # without error (and passes rubocop). However, using parenthesis 
  # around a lambda block looks weird IMO
  scope(cool_users lambda do |arg|
    # some long query
  end)

  # Good - replacing do / end with {} functions without error, passes 
  # rubocop and looks good IMO
  scope cool_users lambda { |arg|
    # some long query
  }
end
Clark Taylor
  • 323
  • 2
  • 10