Ruby 2.0.0 supports keyword arguments (KA) and I wonder what the benefits/use-cases are of this feature in context of pure Ruby, especially when seen in light of the performance penalty due to the keyword matching that needs to be done every time a method with keyword arguments is called.
require 'benchmark'
def foo(a:1,b:2,c:3)
[a,b,c]
end
def bar(a,b,c)
[a,b,c]
end
number = 1000000
Benchmark.bm(4) do |bm|
bm.report("foo") { number.times { foo(a:7,b:8,c:9) } }
bm.report("bar") { number.times { bar(7,8,9) } }
end
# user system total real
# foo 2.797000 0.032000 2.829000 ( 2.906362)
# bar 0.234000 0.000000 0.234000 ( 0.250010)
Keyword arguments have a few distinct advantages no one has touched on.
First off you are not coupled to the order of the arguments. So in a case where you might have a nil argument occasionally it looks a lot cleaner:
def print_greeting(name, message = "Hello")
puts "#{message}, #{name}"
end
print_greeting("John Connor", "Hasta la vista")
If you use keyword arguments:
def print_greeting(message: "Hello", name:)
puts "#{message}, #{name}"
end
print_greeting(message: "Hasta la vista", name: "John Connor")
or even
print_greeting(name: "John Connor", message: "Goodbye")
It removes the need to have to remember the order of the arguments. However, the disadvantage is you have to remember the argument's name. This should be more or less intuitive, and arguably results in more carefully considered method signatures.
Another benefit to using keyword arguments is when you have a method that could require additional arguments in the future.
def create_person(name:, age:, height:)
# make yourself some friends
end
What if your system requirements now need to know about a person's favorite candy bar, or if they are overweight (from consuming too many of their favorite candy bars). How could you use keyword args to do that? Simple:
def create_person(name:, age:, height:, favorite_candy:, overweight: true)
# make yourself some fat friends
end
Before keyword arguments there was always the hash, but that led to a lot more boilerplate code to extract and assign variable. Boilerplate code == more typing == more potential typos == less times writing awesome ruby code.
def old_way(name, opts={})
age = opts[:age]
height = opts[:height]
# all the benefits as before, more arthritis and headaches
end
If you are just setting up a method that takes one argument and will most likely never have a need to change:
def say_full_name(first_name, last_name)
puts "#{first_name} #{last_name}"
end
Then keyword arguments should be avoided, since there is a small performance hit.