Understanding The Behaviour of Inject Used with a Lambda in Ruby

Understanding the behaviour of inject used with a lambda in Ruby

So the reason that

(5..10).map &mult4

works and

(5..10).inject(2) &multL

doesn't is that ruby parens are implicit in the first case, so it really means

(5..10).map(&mult4)

if you wanted, for the second case you could use

(5..10).inject 2, &multL

The outside the parens trick only works for passing blocks to a method, not lambda objects.

Why can't I pass a block variable to inject?

Thanks for pointing out the related question! My googling and the site-search mechanism didn't point me there before. It helped me see I'd just made a dumb syntax error earlier (I tried a call of (1..5).inject([]) proc) that made me misunderstand what I was seeing. So what I needed was in fact:

def wrapper proc
(1..5).inject 0, proc
end

Ruby lambda arguments

Lambdas are weird like that, their behavior is different when you have less than two arguments. Check this article for more information.

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.

How do I using instance variables from within a lambda/Proc defined in a class variable?

You could use instance_exec to supply the appropriate context for the lambdas when you call them, look for the comments to see the changes:

class Actions
# Move the lambdas to a class variable, a COMMANDS constant
# would work just as well and might be more appropriate.
@@commands = {
"ADD" => ->(name) { @people << name },
"REMOVE" => ->(n = 0) { puts "Goodbye" },
"OTHER" => ->(n = 0) { puts "Do Nothing" }
}
def initialize
@people = [ ]
end
def run_command(cmd, *param)
# Use instance_exec and blockify the lambdas with '&'
# to call them in the context of 'self'. Change the
# @@commands to COMMANDS if you prefer to use a constant
# for this stuff.
instance_exec(param, &@@commands[cmd]) if @@commands.key?(cmd)
end
def people
@people
end
end

A lambda expression in a rails validation statement

Your lambda doesn't make a lot of sense, because you're using symbols everywhere - :start_date == Date.today will always be false. The value of lambdas in validations is that they're passed your Event instance, so you can reference event.start_date. I agree with @arthur.karganyan in the comments that if you need something this complicated, it'll be easier to work with as a method.

But, you're also making this much more complicated than you need to. It looks like you have start_date and time as separate attributes, which is difficult to work with because they'll tend to rely on each other. I'd recommend making those a single start_time attribute instead, and then you can use validates_timeliness like so:

validates_datetime :start_time, on_or_after: -> { Time.now }

If you must have separate attributes, your validations might look like:

validates_date :start_date, on_or_after: -> { Date.today }
validates_time :time, on_or_after: lambda { |event| Time.now if event.start_date.today? }

This validates that the date is today or in the future, and if it's today, also checks the time. validates_timeliness appears to accept any time when on_or_after lambda evaluates to nil.

proc return vs lambda return

You should see comment in this answer https://stackoverflow.com/a/723/4576274.

It states

A lambda is an anonymous method. Since it's a method, it returns a
value, and the method that called it can do with it whatever it wants,
including ignoring it and returning a different value.

A Proc is like
pasting in a code snippet. It doesn't act like a method. So when a
return happens within the Proc, that's just part of the code of the
method that called it



Related Topics



Leave a reply



Submit