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)
What are keyword arguments? Keyword arguments are a feature in Ruby 2.0 and higher. They're an alternative to positional arguments, and are really similar (conceptually) to passing a hash to a function, but with better and more explicit errors.
Named parameters provides us the relaxation to remember or to look up the order of parameters in the parameter lists of called methods. The parameter for each argument can be specified by parameter name. Using named parameters in C#, we can put any parameter in any sequence as long as the name is there.
So when you want to pass keyword arguments, you should always use foo(k: expr) or foo(**expr) . If you want to accept keyword arguments, in principle you should always use def foo(k: default) or def foo(k:) or def foo(**kwargs) .
Keyword arguments (or named arguments) are values that, when passed into a function, are identifiable by specific parameter names. A keyword argument is preceded by a parameter and the assignment operator, = . Keyword arguments can be likened to dictionaries in that they map a value to a keyword.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With