17

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) }

4 Answers 4

27

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) }
Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

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.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.