views:

205

answers:

8

I see that in Ruby (and dynamically typed languages, in general) a very common practice is to pass a hash, instead of declaring concrete method parameters. For example, instead of declaring a method with parameters and calling it like this:

def my_method(width, height, show_border)
my_method(400, 50, false)

you can do it this way:

def my_method(options)
my_method({width => 400, height => 50, show_border => false})

I'd like to know your opinion about it. Is it a good or a bad practice, should we do it or not? In what situation using this practice is valid, and it what situation can it be dangerous?

A: 

It is a good practice. You don't need to think about method signature and the order of the arguments. Another advantage is that you can easily omit the arguments you do not want to enter. You can take a look at the ExtJS framework as it is using this type of argument passing extensively.

Li0liQ
+1  A: 

I think this method of parameter passing is much clearer when there are more than a couple of parameters or when there are a number of optional parameters. It essentially makes method calls manifestly self-documenting.

Rich
A: 

It is not common practice in Ruby to use a hash rather than formal parameters.

I think this is being confused with the common pattern of passing a hash as a parameter when the parameter can take a number of values e.g. setting attributes of a Window in a GUI toolkit.

If you have a number of arguments to your method or function then explicitly declare them and pass them. You get the benefit that the interpreter will check that you have passed all the arguments.

Don't abuse the language feature, know when to use it and when not to use it.

Chris McCauley
If you need variable number of arguments just use `def method(*args)`, hashes don't solve that particular problem
MBO
Thanks for pointing out the potential confusion. I was really thinking of the case the original poster raised where the order and number of parameters is variable.
Chris McCauley
+2  A: 

Ruby has implicit hash parameters, so you could also write

def my_method(options = {}) 

my_method(:width => 400, :height => 50, :show_border => false)

and with Ruby 1.9 and new hash syntax it can be

my_method( width: 400, height: 50, show_border: false )

When a function takes more than 3-4 parameters, it's much easier to see which is what, without counting the respective positions.

MBO
+1  A: 

It's a trade-off. You lose some clarity (how do I know what params to pass) and checking (did I pass the right number of arguments?) and gain flexibility (the method can default params it doesn't receive, we can deploy a new version that takes more params and break no existing code)

You can see this question as part of the larger Strong/Weak type discussion. See Steve yegge's blog here. I've used this style in C and C++ in cases where I wanted to support quite flexible argument passing. Arguably a standard HTTP GET, with some query parameters is exactly this style.

If you go for the hash appraoch I'd say that you need to make sure your testing is really good, problems with incorrectly spelled parameter names will only show up at run time.

djna
+1  A: 

Both approaches have their own advantages and disadvantages, when you use an options hash replacing standard arguments you lose clarity in the code defining the method but gain clarity whenever you use the method because of the pseudo-named paramaters created by using an options hash.

My general rule is if you either have a lot of arguments for a method (more than 3 or 4) or lots of optional arguments then use an options hash otherwise use standard arguments. However when using an options hash it is important to always include a comment with the method definition describing the possible arguments.

benofsky
A: 

I'd say that if you are either:

  1. Having more than 6 method parameters
  2. Passing options that have some required, some optional and some with default values

You would most probably want to use a hash. It's much easier to see what the arguments mean without looking up in the documentation.

To those of you saying it's hard to figure what options a method takes, that just means that the code is poorly documented. With YARD, you can use the @option tag to specify options:

##
# Create a box.
#
# @param [Hash] options The options hash.
# @option options [Numeric] :width The width of the box.
# @option options [Numeric] :height The height of the box.
# @option options [Boolean] :show_border (false) Whether to show a
#   border or not.
def create_box(options={})
  options[:show_border] ||= false
end

But in that specific example there's such few and simple parameters, so I think I'd go with this:

##
# Create a box.
#
# @param [Numeric] width The width of the box.
# @param [Numeric] height The height of the box.
# @param [Boolean] show_border Whether to show a border or not.
def create_box(width, height, show_border=false)
end
dvyjones
A: 

I'm sure no one using dynamic languages cares, but think about the performance penalty your program is going to be hit with when you start passing hashes to functions.

The interpreter may possibly be smart enough to create a static const hash object and only reference it by pointer, if the code is using a hash with all members that are source code literals.

But if any of those members are variables then the hash must be reconstructed each time it is called.

I've done some Perl optimizations and this kind of thing can become noticeable in inner code loops.

Function parameters perform much better.

Zan Lynx