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, thereturn
statement returns only from the proc itself - In a
Proc.new
-created proc, thereturn
statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!
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
:
An example:Equivalent to Proc.new, except the resulting Proc objects check the number of parameters passed when called.
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
How Do Erlang Actors Differ from Oop Objects
Get Route for Base Class of Sti Class in Rails
When Is The Enumerator::Yielder#Yield Method Useful
Ruby Rspec. Get List of All Test
Parsing a CSV File Using Different Encodings and Libraries
How to Have Two Columns in One Table Point to The Same Column in Another with Activerecord
Monitor Multiple Rails Applications
How to Access a Toplevel Entity in Ruby, from Inside a Module Which Defines The Same Name
Escaping Strings for Ruby Sqlite Insert
Ruby Readline Fails If Process Started with Arguments
Where Are Keywords Defined in Ruby
Mongodb Server Doesn't Start at Gitlab Runner Using Gitlab-Ci
How to Resize Image Only If Width Exceeds with Graphics/Image Magick
Automatically Adding Proxy to All Http Connections in Ruby
In Ruby, How to Be Warned of Duplicate Keys in Hashes When Loading a Yaml Document