More Advanced Ruby Method Arguments – Hashes And Block Basics

ArgumentsI’ve previously given an overview of basic method arguments in Ruby (at least in Ruby 1.9). There is quite a lot you can do with just the basic method arguments, so I purposely left out the more advanced topics from that post (which many people were quick to point out :)). However, if you want to have an in-depth knowledge of Ruby you will need to know how to use hashes as method arguments as well as where blocks fit into the picture and this is what I am going to cover here.

Using Hashes As Arguments

A Hash is just a regular object in Ruby, so normally, using it as an argument is no different from using any other object as an argument e.g.:

def some_method(a, my_hash, b)
  p a
  p my_hash
  p b
end
 
some_method "Hello", {:first=>"abc", :second=>"123"},"World"

This would produce the following output:

"Hello"
{:first=>"abc", :second=>"123"}
"World"

The interesting things about hashes as arguments is that depending on their location in the argument list you can get some interesting benefits (and sometimes interesting detriments).

Hashes As The Last Argument

When you use a hash as the last argument in the list Ruby allows you to forego the use of the curly braces which surprisingly can make argument lists look a lot nicer, consider this:

def print_name_and_age(age, name_hash)
  p "Name: #{name_hash[:first]} #{name_hash[:middle]} #{name_hash[:last]}"
  p "Age: #{age}"
end
 
print_name_and_age 25, :first=>'John', :middle=>'M.', :last=>'Smith'

This produces the following output:

"Name: John M. Smith"
"Age: 25"

We don’t need to use curly braces as the hash is the last argument which makes our method call look a little neater. This also has a very neat side-effect of making the method arguments somewhat self-documenting (the hash key tells you what the argument value relates to). If we weren’t using a hash, then we would have to know the exact order of the method arguments to pass them in (e.g. does last name come first in the argument list, or is it first name) e.g.:

def print_name_and_age(age, last, middle, first)
  p "Name: #{first} #{middle} #{last}"
  p "Age: #{age}"
end
 
print_name_and_age 25, 'John', 'M.', 'Smith'

This looks correct, but we no longer know for sure if we accidentally confused the order of the parameters (which we did in this case) unless we look at the method definition, so our output is not what we expect:

"Name: Smith M. John"
"Age: 25"

This feature of not having to use curly braces also works when we use a hash as the only argument to our method. Because of this and the self-documenting properties of passing in a hash as an argument, a case can be made for exclusively using a hash to pass all arguments to a method rather than having an argument list. Not only do we get the benefits of self-documentation and neater syntax, but we can also pass arguments in any order since the hash doesn’t care due to the key/value association.

So Why Not Do This All The Time

When you use a hash as an argument you always have the extra overhead of using a hash, i.e. you always have to pull values from the hash, inside the method, using the [] operator. It is probably not such a big deal, but it is there nonetheless, so it may not be worth doing this for methods that have one or two simple arguments.

There is one more caveat to be aware of with hashes. Just like you get some niceness when you use the hash as a last (or only) argument in an argument list, you get some nastiness when you use it as the first. If you use a hash as the first argument, you can’t leave out the curly braces, not only that, because there are other arguments in the list you can’t leave out the parentheses from the method call like you usually would with Ruby e.g.:

def print_name_and_age(name_hash, age)
  p "Name: #{name_hash[:first]} #{name_hash[:middle]} #{name_hash[:last]}"
  p "Age: #{age}"
end
 
print_name_and_age({:first=>'John', :middle=>'M.', :last=>'Smith'}, 25)

You can probably guess that if you did try to leave off the parentheses, then Ruby would try to interpret your hash as a block (as curly braces are also used for block syntax) and so we must use the them to disambiguate our method call. This leads me neatly into discussing block and exactly what they are (and aren’t).

Blocks Are NOT Method Arguments

Despite what some people might believe, blocks are not method arguments. Blocks and arguments are two separate constructs. Infact if you have even a basic understanding of blocks you know that blocks can have arguments of their own. Does it really make sense for arguments to have arguments?

But to get back to block basics. There are many methods in ruby that iterate over a range of values. Most of these iterators are written in such a way as to be able to take a code block as part of their calling syntax. The method can then yield control to the code block (i.e. execute the block) during execution as many times as is necessary for the iteration to complete (e.g. if we are iterating over array values, we can execute the block as many times as there are array values etc.).

There are two types of block syntax, curly brace and do..end. If we want to supply a one-liner block to a method we normally us the curly brace syntax, otherwise we use the do..end syntax. For example:

[1,2,3,4].each {p 'hello'}

or

[1,2,3,4].each do
  print 'i am printing '
  puts 'hello'
end

The two versions of block syntax are not exactly alike, but I will cover that aspect of blocks in a later post (which I plan to devote completely to blocks).

The other side of the coin when it comes to blocks is the yield keyword. Any method that wants to take a block as a parameter can use the yield keyword to execute the block at any time. It’s as simple as that. This one is another thing that I would like to explore further in a later post.

One final thing to remember about block basics is the fact that blocks can also take parameters e.g.:

[1,2,3,4].each do |x|
  print "i am printing #{x} "
  puts "hello"
end

The above will produce the following output:

i am printing 1 hello
i am printing 2 hello
i am printing 3 hello
i am printing 4 hello

Depending on what you’re iterating over the parameters that get passed to the blocks can have different values (and there can even be a different number of parameters). In the above case, because we are iterating over an array, each time the block is executed the parameter contains the value of the current element of the array that we are iterating over. As you can see you don’t pass parameters to blocks using parentheses like you would to methods, instead you pass them in between two pipes (i.e. |x|). There is once again much more to be said about blocks and block arguments, but since we are only covering block basics here, I’ll leave that for later.

The main things to take away from this are as follows:

  • blocks are not method arguments but are infact a separate construct
  • blocks are used to support iterator-type methods
  • there are two types of block syntax
  • blocks can take parameters of their own

I will explore all of those in more depth when I dig into the how and why of blocks in Ruby. I hope you found at least some of this helpful/interesting and as always if you have something to add (or if you just want to say hello :)), feel free to leave a comment below.

Image by Aislinn Ritchie

  • Pingback: Dew Drop – August 26, 2009 | Alvin Ashcraft's Morning Dew

  • http://www.lukematthewsutton.com Luke Matthew Sutton

    “Blocks Are NOT Method Arguments”

    Well, yes they _can_ be actually. If you define a method that accepts a block, you can create a parameter for it. You’re then free to call it, or pass it to another method, just like any other argument.

    The only kink is the need to prefix the block parameter with ‘&’ and that you can only have the one.

    def accept_block(first, second, &blk)
    blk.call(“do something block”)
    end

    Most iterators just use the yield construct, in which case the block isn’t assigned to an argument.

    “Does it really make sense for arguments to have arguments?”

    Yep, those are called first-class-functions in other languages. A block is essentially an anonymous function which you pass into a method — with some special syntax to make it easier and look neater.

    • http://www.skorks.com Alan Skorkin

      I wasn’t aware you could do that with blocks, I might dig some more into that, sounds like a cool feature, thanks.

      I am aware of first class function and yeah I agree with you if an argument is a function it certainly makes sense for it to be able to have arguments. I guess my colorful way of making a point wasn’t as clever as I thought it was :).

      • Dubl

        ok then so to extend the thought, here’s how to turn a block into an object and use it:

        def objectify(&ablock)
        ablock
        end

        block_object=objectify{|x| x*2}

        block_object.call(5) #=>10

        def use(block_obj,param)
        puts block_obj.call(param)
        end

        use(block_object,4) # =>8
        use(block_object,3) #=>6
        block_object=objectify{|x| x*10}
        use(block_object,4) #=>40

        • Hari

          > block_object=objectify{|x| x*2}
          Should have been:

          block_object=lambda{|x| x*2}

  • Guoliang Cao

    Nice writeup!

    I would add one more thing though: if use hash as argument, default values are set differently.

    E.g. for regular argument with default value, it will look like this

    def do_something timeout = 30, retry = 3

    end

    If use hash, in order to achieve same behavior, it should be

    def do_something options = {}
    default_options = {:timeout => 30, :retry => 3}
    options = default_options.merge(options)

    end

    That’s a lot more typing.

    • http://www.skorks.com Alan Skorkin

      I haven’t really considered default arguments when using hashes, but it makes sense that you should be able to do it and from what you say it is definitely a lot more verbose which is a point against it (if you need default values when using hashes as that is).

  • Pingback: Ennuyer.net » Blog Archive » Rails Reading - August 30, 2009

  • roger

    also checkout “named arguments”
    http://github.com/maca/arguments

  • http://www.emersonlackey.com Emerson Lackey

    I find myself back at this post at least once a month. Landmark to say the least… thanks for the great writeup and pleasing syntax highlighter!

    • http://www.skorks.com Alan Skorkin

      No worries, I am glad you find it helpful

      • gwhosubex

        Yes which syntax highlighter program did you use?

  • http://absolutelytech.com Deepak Mittal

    You should probably provide a link to this awesome article on blocks, procs and lambdas:
    http://augustl.com/blog/2008/procs_blocks_and_anonymous_functions

    PS: Not spamming, just a helpful article. Not written by me anyways.

  • atul

    very helpful…thanks a lot