When to Use a Lambda in Ruby on Rails

When to use a lambda in Ruby on Rails?

http://augustl.com/blog/2008/procs_blocks_and_anonymous_functions/ has a run-down of what blocks/procs/lambdas are, how you can use them, and how they compare to functions in other languages. It definitely answers your question.

Do be aware that the last section 'A note on lambdas' mentions a point that is only true in Ruby 1.8 and changed in 1.9 - Ruby: Proc.new { 'waffles' } vs. proc { 'waffles' }

Where and when to use Lambda?

It's true, you don't need anonymous functions (or lambdas, or whatever you want to call them). But there are a lot of things you don't need. You don't need classes—just pass all the instance variables around to ordinary functions. Then

class Foo
attr_accessor :bar, :baz
def frob(x)
bar = baz*x
end
end

would become

def new_Foo(bar,baz)
[bar,baz]
end

def bar(foo)
foo[0]
end
# Other attribute accessors stripped for brevity's sake

def frob(foo,x)
foo[0] = foo[1]*x
end

Similarly, you don't need any loops except for loop...end with if and break. I could go on and on.1 But you want to program with classes in Ruby. You want to be able to use while loops, or maybe even array.each { |x| ... }, and you want to be able to use unless instead of if not.

Just like these features, anonymous functions are there to help you express things elegantly, concisely, and sensibly. Being able to write some_function(lambda { |x,y| x + f(y) }) is much nicer than having to write

def temp(x,y)
x + f(y)
end
some_function temp

It's much bulkier to have to break off the flow of code to write out a deffed function, which then has to be given a useless name, when it's just as clear to write the operation in-line. It's true that there's nowhere you must use a lambda, but there are lots of places I'd much rather use a lambda.

Ruby solves a lot of the lambda-using cases with blocks: all the functions like each, map, and open which can take a block as an argument are basically taking a special-cased anonymous function. array.map { |x| f(x) + g(x) } is the same as array.map(&lambda { |x| f(x) + g(x) }) (where the & just makes the lambda "special" in the same way that the bare block is). Again, you could write out a separate deffed function every time—but why would you want to?

Languages other than Ruby which support that style of programming don't have blocks, but often support a lighter-weight lambda syntax, such as Haskell's \x -> f x + g x, or C#'s x => f(x) + g(x);2. Any time I have a function which needs to take some abstract behavior, such as map, or each, or on_clicked, I'm going to be thankful for the ability to pass in a lambda instead of a named function, because it's just that much easier. Eventually, you stop thinking of them as somehow special—they're about as exciting as literal syntax for arrays instead of empty().append(1).append(2).append(3). Just another useful part of the language.


1: In the degenerate case, you really only need eight instructions: +-<>[].,. <> move an imaginary "pointer" along an array; +- increment and decrement the integer in the current cell; [] perform a loop-while-non-zero; and ., do input and output. In fact, you really only need just one instruction, such as subleq a b c (subtract a from b and jump to c if the result is less than or equal to zero).

2: I've never actually used C#, so if that syntax is wrong, feel free to correct it.

When to use lambda when evaluating Time

Scopes are evaluated when the class is loaded. A production server for example loads the code once and runs with the same evaluated time until the server is reloaded or restarted. Therefore you should use lambdas in scopes.

When you have a query in a method or a rake task than the code evaluated every time. Therefore you do not need to use lambdas in that case.

When to use lambda, when to use Proc.new?

Another important but subtle difference between procs created with lambda and procs created with Proc.new is how they handle the return statement:

  • In a lambda-created proc, the return statement returns only from the proc itself
  • In a Proc.new-created proc, the return statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!

Here's lambda-created proc's return in action. It behaves in a way that you probably expect:

def whowouldwin

mylambda = lambda {return "Freddy"}
mylambda.call

# mylambda gets called and returns "Freddy", and execution
# continues on the next line

return "Jason"

end


whowouldwin
#=> "Jason"

Now here's a Proc.new-created proc's return doing the same thing. You're about to see one of those cases where Ruby breaks the much-vaunted Principle of Least Surprise:

def whowouldwin2

myproc = Proc.new {return "Freddy"}
myproc.call

# myproc gets called and returns "Freddy",
# but also returns control from whowhouldwin2!
# The line below *never* gets executed.

return "Jason"

end


whowouldwin2
#=> "Freddy"

Thanks to this surprising behavior (as well as less typing), I tend to favor using lambda over Proc.new when making procs.

What's the difference between a proc and a lambda in Ruby?

One difference is in the way they handle arguments. Creating a proc using proc {} and Proc.new {} are equivalent. However, using lambda {} gives you a proc that checks the number of arguments passed to it. From ri Kernel#lambda:

Equivalent to Proc.new, except the resulting Proc objects check the number of parameters passed when called.

An example:

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

In addition, as Ken points out, using return inside a lambda returns the value of that lambda, but using return in a proc returns from the enclosing block.

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

So for most quick uses they're the same, but if you want automatic strict argument checking (which can also sometimes help with debugging), or if you need to use the return statement to return the value of the proc, use lambda.

Difference between lambda and - operator in Ruby

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

When to and when not to use lambda for named scopes?


In Rails 4

Always use lambda. The second syntax is incorrect in Rails 4 and will throw an error (undefined method 'call' for ActiveRecord::Relation)

# activerecord/lib/active_record/scoping/named.rb
scope = all.scoping { body.call(*args) }

In Rails 3

scope method behaves same way in both cases - it created a new class method called credits. Difference is that when given a lambda, it evaluates this lambda every time this new method is called to get the scope, while when given relation, it just uses what has been passed.

# activerecord/lib/active_record/named_scope.rb
options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options

In this case, lambda always return exactly same relation, so no difference will be noted.

Lambda notation is used usually to pass arguments to the scope:

scope :before, lambda {|date| where.created_at < date}

Which then can be used like:

Model.before(1.day.ago)

This is naturally impossible to write without the lambda.



Related Topics



Leave a reply



Submit