When to Use Keyword Arguments Aka Named Parameters in Ruby

What is the current status of named arguments (named method parameters, keyword arguments) in Ruby?

Update: Here's a great 6mins screencast by Peter Cooper on Keyword Arguments in Ruby 2.0.

Old: Have a look at this blog post about Keyword Arguments in Ruby 2.0 from yesterday.

Is there a way to do recursion with keyword arguments in ruby 2 without re-specifying each argument?

Not sure if a built-in method exists to do so, but it's evidently possible using local_variables and some eval-fu:

def foo(bar: :baz)
Hash[local_variables.map { |k| [k, eval("#{k}")] }]
end

foo # {:bar=>:baz}

Related question:

How do I dynamically create a local variable in Ruby?

How not to mix hash and keywordArgument in a Ruby function?

The tutorial is not saying that using => (AKA "hash-rocket") is bad, it's saying the use of Strings or other objects besides symbols (:foo) uses more memory.

{ :foo => 'bar' }

is the original way of defining a symbol as a key. Compare these two definitions:

{ :foo => 'bar' } # => {:foo=>"bar"}
{ foo: 'bar' } # => {:foo=>"bar"}

We tend to use foo: because we're lazy.

Symbols don't cost as much, but that's a different subject and why can be found with a little searching.

As developers we need to understand the cost of using one algorithm or object versus another. Using Strings or whatever as a key has its time and place and knowing those can save a lot of memory or development time.

Finally, style guides are useful; Read several of them periodically and consider what they're suggesting. The guides are aimed at teams to help them write in a common and consistent manner following good coding practices adapted for Ruby, however they're not cast in stone. Knowing why they're recommended is important, because occasionally we have to ignore or bend the rules, but, when we do, we better be ready to explain why in a code-review.

And, if you're not familiar with Ruby's coding styles, I'd recommend learning about Rubocop, which is a nice tool for checking for consistent and accepted programming style and catching errors.

Avoid repeating named argument defaults when calling superclass initializer in Ruby (2.1+)

In Ruby 2.0+, you can use the double splat operator.

def initialize(bar: 456, **args)
super(**args)
@bar = bar
end

An example:

[1] pry(main)> class Parent
[1] pry(main)* def initialize(a: 456)
[1] pry(main)* @a = a
[1] pry(main)* end
[1] pry(main)* end
=> :initialize
[2] pry(main)> class Child < Parent
[2] pry(main)* def initialize(b: 789, **args)
[2] pry(main)* super(**args)
[2] pry(main)* @b = b
[2] pry(main)* end
[2] pry(main)* end
=> :initialize
[3] pry(main)> ch = Child.new(b: 3)
=> #<Child:0x007fc00513b128 @a=456, @b=3>
[4] pry(main)> ch = Child.new(b: 3, a: 6829)
=> #<Child:0x007fc00524a550 @a=6829, @b=3>

The double splat operator is similar to the single splat operator, but instead of capturing all of the extra args into an array, it captures them into a hash. Then when used as an argument to super, the double splat flattens the hash into named parameters, kind of like the single splat does for arrays.

Passing a hash as an initial value when creating an object

Your initialize method is looking for two parameters, but your input contains only one, the hash. Here's how you can handle a hash containing Fahrenheit:

  def initialize(temp)
@f = temp[:f]
end

If you want to handle input in either F or C, I'll leave that as an exercise for you.

Positional argument vs keyword argument

That text you quote seems to be confused about two totally different things:

  • Positional and keyword arguments are a feature of calls to a function (see Python reference section 5.3.4 Calls).
  • Default values are a feature of function definitions, as per section 7.6 Function definitions

I suspect the people who put together that course-ware weren't totally familiar with Python :-) Hence that link you provide is not a very good quality one.


In your call to your function, you're using the "keyword argument" feature (where the argument is named rather than relying on its position). Without that, values are bound to names based on order alone. So, in this example, the two calls below are equivalent:

def process_a_and_b(a, b):
blah_blah_blah()

process_a_and_b(1, 2)
process_a_and_b(b=2, a=1)

By further way of example, refer to the following definition and calls:

def fn(a, b, c=1):        # a/b required, c optional.
return a * b + c

print(fn(1, 2)) # returns 3, positional and default.
print(fn(1, 2, 3)) # returns 5, positional.
print(fn(c=5, b=2, a=2)) # returns 9, named.
print(fn(b=2, a=2)) # returns 5, named and default.
print(fn(5, c=2, b=1)) # returns 7, positional and named.
print(fn(8, b=0)) # returns 1, positional, named and default.


Related Topics



Leave a reply



Submit